1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include "xml/attribute-record.h"
55 #include <glibmm/i18n.h>
56 #include "helper/unit-menu.h"
57 #include "helper/units.h"
58 #include "live_effects/effect.h"
60 #include "inkscape.h"
61 #include "conn-avoid-ref.h"
64 #include "select-toolbar.h"
65 #include "gradient-toolbar.h"
67 #include "connector-context.h"
68 #include "node-context.h"
69 #include "draw-context.h"
70 #include "shape-editor.h"
71 #include "tweak-context.h"
72 #include "sp-rect.h"
73 #include "box3d.h"
74 #include "box3d-context.h"
75 #include "sp-star.h"
76 #include "sp-spiral.h"
77 #include "sp-ellipse.h"
78 #include "sp-text.h"
79 #include "sp-flowtext.h"
80 #include "sp-clippath.h"
81 #include "sp-mask.h"
82 #include "style.h"
83 #include "tools-switch.h"
84 #include "selection.h"
85 #include "selection-chemistry.h"
86 #include "document-private.h"
87 #include "desktop-style.h"
88 #include "../libnrtype/font-lister.h"
89 #include "../libnrtype/font-instance.h"
90 #include "../connection-pool.h"
91 #include "../prefs-utils.h"
92 #include "../inkscape-stock.h"
93 #include "icon.h"
94 #include "graphlayout/graphlayout.h"
95 #include "interface.h"
96 #include "shortcuts.h"
98 #include "mod360.h"
100 #include "toolbox.h"
102 #include "flood-context.h"
104 #include "ink-action.h"
105 #include "ege-adjustment-action.h"
106 #include "ege-output-action.h"
107 #include "ege-select-one-action.h"
108 #include "helper/unit-tracker.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);
136 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
139 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
140 static Inkscape::IconSize sizeChoices[] = {
141 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
142 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
143 Inkscape::ICON_SIZE_MENU
144 };
145 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
146 return sizeChoices[index];
147 }
149 static struct {
150 gchar const *type_name;
151 gchar const *data_name;
152 sp_verb_t verb;
153 sp_verb_t doubleclick_verb;
154 } const tools[] = {
155 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
156 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
157 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
158 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
159 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
160 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
161 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
162 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
163 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
164 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
165 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
166 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
167 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
168 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
169 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
170 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
171 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
172 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
173 { NULL, NULL, 0, 0 }
174 };
176 static struct {
177 gchar const *type_name;
178 gchar const *data_name;
179 GtkWidget *(*create_func)(SPDesktop *desktop);
180 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
181 gchar const *ui_name;
182 gint swatch_verb_id;
183 gchar const *swatch_tool;
184 gchar const *swatch_tip;
185 } const aux_toolboxes[] = {
186 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
187 SP_VERB_INVALID, 0, 0},
188 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
189 SP_VERB_INVALID, 0, 0},
190 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
191 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
192 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
193 SP_VERB_INVALID, 0, 0},
194 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
195 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
196 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
197 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
198 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
199 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
200 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
201 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
202 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
203 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
204 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
205 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
206 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
207 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
208 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
209 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
210 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
211 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
212 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
213 SP_VERB_INVALID, 0, 0},
214 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
215 SP_VERB_INVALID, 0, 0},
216 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
217 SP_VERB_INVALID, 0, 0},
218 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
219 SP_VERB_INVALID, 0, 0},
220 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
221 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
222 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
223 };
225 #define TOOLBAR_SLIDER_HINT "full"
227 static gchar const * ui_descr =
228 "<ui>"
229 " <toolbar name='SelectToolbar'>"
230 " <toolitem action='EditSelectAll' />"
231 " <toolitem action='EditSelectAllInAllLayers' />"
232 " <toolitem action='EditDeselect' />"
233 " <separator />"
234 " <toolitem action='ObjectRotate90CCW' />"
235 " <toolitem action='ObjectRotate90' />"
236 " <toolitem action='ObjectFlipHorizontally' />"
237 " <toolitem action='ObjectFlipVertically' />"
238 " <separator />"
239 " <toolitem action='SelectionToBack' />"
240 " <toolitem action='SelectionLower' />"
241 " <toolitem action='SelectionRaise' />"
242 " <toolitem action='SelectionToFront' />"
243 " <separator />"
244 " <toolitem action='XAction' />"
245 " <toolitem action='YAction' />"
246 " <toolitem action='WidthAction' />"
247 " <toolitem action='LockAction' />"
248 " <toolitem action='HeightAction' />"
249 " <toolitem action='UnitsAction' />"
250 " <separator />"
251 " <toolitem action='transform_affect_label' />"
252 " <toolitem action='transform_stroke' />"
253 " <toolitem action='transform_corners' />"
254 " <toolitem action='transform_gradient' />"
255 " <toolitem action='transform_pattern' />"
256 " </toolbar>"
258 " <toolbar name='NodeToolbar'>"
259 " <toolitem action='NodeInsertAction' />"
260 " <toolitem action='NodeDeleteAction' />"
261 " <separator />"
262 " <toolitem action='NodeJoinAction' />"
263 " <toolitem action='NodeBreakAction' />"
264 " <separator />"
265 " <toolitem action='NodeJoinSegmentAction' />"
266 " <toolitem action='NodeDeleteSegmentAction' />"
267 " <separator />"
268 " <toolitem action='NodeCuspAction' />"
269 " <toolitem action='NodeSmoothAction' />"
270 " <toolitem action='NodeSymmetricAction' />"
271 " <separator />"
272 " <toolitem action='NodeLineAction' />"
273 " <toolitem action='NodeCurveAction' />"
274 " <separator />"
275 " <toolitem action='ObjectToPath' />"
276 " <toolitem action='StrokeToPath' />"
277 " <separator />"
278 " <toolitem action='NodeXAction' />"
279 " <toolitem action='NodeYAction' />"
280 " <toolitem action='NodeUnitsAction' />"
281 " <separator />"
282 " <toolitem action='ObjectEditClipPathAction' />"
283 " <toolitem action='ObjectEditMaskPathAction' />"
284 " <toolitem action='EditNextLPEParameterAction' />"
285 " <separator />"
286 " <toolitem action='NodesShowHandlesAction' />"
287 " <toolitem action='NodesShowHelperpath' />"
288 " </toolbar>"
290 " <toolbar name='TweakToolbar'>"
291 " <toolitem action='TweakWidthAction' />"
292 " <separator />"
293 " <toolitem action='TweakForceAction' />"
294 " <toolitem action='TweakPressureAction' />"
295 " <separator />"
296 " <toolitem action='TweakModeAction' />"
297 " <separator />"
298 " <toolitem action='TweakFidelityAction' />"
299 " <separator />"
300 " <toolitem action='TweakChannelsLabel' />"
301 " <toolitem action='TweakDoH' />"
302 " <toolitem action='TweakDoS' />"
303 " <toolitem action='TweakDoL' />"
304 " <toolitem action='TweakDoO' />"
305 " </toolbar>"
307 " <toolbar name='ZoomToolbar'>"
308 " <toolitem action='ZoomIn' />"
309 " <toolitem action='ZoomOut' />"
310 " <separator />"
311 " <toolitem action='Zoom1:0' />"
312 " <toolitem action='Zoom1:2' />"
313 " <toolitem action='Zoom2:1' />"
314 " <separator />"
315 " <toolitem action='ZoomSelection' />"
316 " <toolitem action='ZoomDrawing' />"
317 " <toolitem action='ZoomPage' />"
318 " <toolitem action='ZoomPageWidth' />"
319 " <separator />"
320 " <toolitem action='ZoomPrev' />"
321 " <toolitem action='ZoomNext' />"
322 " </toolbar>"
324 " <toolbar name='StarToolbar'>"
325 " <separator />"
326 " <toolitem action='StarStateAction' />"
327 " <separator />"
328 " <toolitem action='FlatAction' />"
329 " <separator />"
330 " <toolitem action='MagnitudeAction' />"
331 " <toolitem action='SpokeAction' />"
332 " <toolitem action='RoundednessAction' />"
333 " <toolitem action='RandomizationAction' />"
334 " <separator />"
335 " <toolitem action='StarResetAction' />"
336 " </toolbar>"
338 " <toolbar name='RectToolbar'>"
339 " <toolitem action='RectStateAction' />"
340 " <toolitem action='RectWidthAction' />"
341 " <toolitem action='RectHeightAction' />"
342 " <toolitem action='RadiusXAction' />"
343 " <toolitem action='RadiusYAction' />"
344 " <toolitem action='RectUnitsAction' />"
345 " <separator />"
346 " <toolitem action='RectResetAction' />"
347 " </toolbar>"
349 " <toolbar name='3DBoxToolbar'>"
350 " <toolitem action='3DBoxAngleXAction' />"
351 " <toolitem action='3DBoxVPXStateAction' />"
352 " <separator />"
353 " <toolitem action='3DBoxAngleYAction' />"
354 " <toolitem action='3DBoxVPYStateAction' />"
355 " <separator />"
356 " <toolitem action='3DBoxAngleZAction' />"
357 " <toolitem action='3DBoxVPZStateAction' />"
358 " </toolbar>"
360 " <toolbar name='SpiralToolbar'>"
361 " <toolitem action='SpiralStateAction' />"
362 " <toolitem action='SpiralRevolutionAction' />"
363 " <toolitem action='SpiralExpansionAction' />"
364 " <toolitem action='SpiralT0Action' />"
365 " <separator />"
366 " <toolitem action='SpiralResetAction' />"
367 " </toolbar>"
369 " <toolbar name='PenToolbar'>"
370 " <toolitem action='FreehandModeActionPen' />"
371 " <separator />"
372 " <toolitem action='SetPenShapeAction'/>"
373 " </toolbar>"
375 " <toolbar name='PencilToolbar'>"
376 " <toolitem action='FreehandModeActionPencil' />"
377 " <separator />"
378 " <toolitem action='PencilToleranceAction' />"
379 " <separator />"
380 " <toolitem action='PencilResetAction' />"
381 " <separator />"
382 " <toolitem action='SetPencilShapeAction'/>"
383 " </toolbar>"
385 " <toolbar name='CalligraphyToolbar'>"
386 " <separator />"
387 " <toolitem action='SetProfileAction'/>"
388 " <separator />"
389 " <toolitem action='CalligraphyWidthAction' />"
390 " <toolitem action='PressureAction' />"
391 " <toolitem action='TraceAction' />"
392 " <toolitem action='ThinningAction' />"
393 " <separator />"
394 " <toolitem action='AngleAction' />"
395 " <toolitem action='TiltAction' />"
396 " <toolitem action='FixationAction' />"
397 " <separator />"
398 " <toolitem action='CapRoundingAction' />"
399 " <separator />"
400 " <toolitem action='TremorAction' />"
401 " <toolitem action='WiggleAction' />"
402 " <toolitem action='MassAction' />"
403 " <separator />"
404 " </toolbar>"
406 " <toolbar name='ArcToolbar'>"
407 " <toolitem action='ArcStateAction' />"
408 " <separator />"
409 " <toolitem action='ArcStartAction' />"
410 " <toolitem action='ArcEndAction' />"
411 " <separator />"
412 " <toolitem action='ArcOpenAction' />"
413 " <separator />"
414 " <toolitem action='ArcResetAction' />"
415 " <separator />"
416 " </toolbar>"
418 " <toolbar name='PaintbucketToolbar'>"
419 " <toolitem action='ChannelsAction' />"
420 " <separator />"
421 " <toolitem action='ThresholdAction' />"
422 " <separator />"
423 " <toolitem action='OffsetAction' />"
424 " <toolitem action='PaintbucketUnitsAction' />"
425 " <separator />"
426 " <toolitem action='AutoGapAction' />"
427 " <separator />"
428 " <toolitem action='PaintbucketResetAction' />"
429 " </toolbar>"
431 " <toolbar name='EraserToolbar'>"
432 " <toolitem action='EraserWidthAction' />"
433 " <separator />"
434 " <toolitem action='EraserModeAction' />"
435 " </toolbar>"
437 " <toolbar name='DropperToolbar'>"
438 " <toolitem action='DropperOpacityAction' />"
439 " <toolitem action='DropperPickAlphaAction' />"
440 " <toolitem action='DropperSetAlphaAction' />"
441 " </toolbar>"
443 " <toolbar name='ConnectorToolbar'>"
444 " <toolitem action='ConnectorAvoidAction' />"
445 " <toolitem action='ConnectorIgnoreAction' />"
446 " <toolitem action='ConnectorSpacingAction' />"
447 " <toolitem action='ConnectorGraphAction' />"
448 " <toolitem action='ConnectorLengthAction' />"
449 " <toolitem action='ConnectorDirectedAction' />"
450 " <toolitem action='ConnectorOverlapAction' />"
451 " </toolbar>"
453 "</ui>"
454 ;
456 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
458 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
460 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
461 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
463 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
464 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
466 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
467 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
470 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
471 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
472 Inkscape::UI::View::View *view, GtkTooltips *tt);
474 class VerbAction : public Gtk::Action {
475 public:
476 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
478 virtual ~VerbAction();
479 virtual void set_active(bool active = true);
481 protected:
482 virtual Gtk::Widget* create_menu_item_vfunc();
483 virtual Gtk::Widget* create_tool_item_vfunc();
485 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
486 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
488 virtual void on_activate();
490 private:
491 Inkscape::Verb* verb;
492 Inkscape::Verb* verb2;
493 Inkscape::UI::View::View *view;
494 GtkTooltips *tooltips;
495 bool active;
497 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
498 };
501 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
502 {
503 Glib::RefPtr<VerbAction> result;
504 SPAction *action = verb->get_action(view);
505 if ( action ) {
506 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
507 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
508 }
510 return result;
511 }
513 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
514 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
515 verb(verb),
516 verb2(verb2),
517 view(view),
518 tooltips(tooltips),
519 active(false)
520 {
521 }
523 VerbAction::~VerbAction()
524 {
525 }
527 Gtk::Widget* VerbAction::create_menu_item_vfunc()
528 {
529 // First call in to get the icon rendered if present in SVG
530 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
531 delete widget;
532 widget = 0;
534 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
535 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
536 return widg;
537 }
539 Gtk::Widget* VerbAction::create_tool_item_vfunc()
540 {
541 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
542 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
543 GtkWidget* toolbox = 0;
544 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
545 SP_BUTTON_TYPE_TOGGLE,
546 verb,
547 verb2,
548 view,
549 tooltips );
550 if ( active ) {
551 sp_button_toggle_set_down( SP_BUTTON(button), active);
552 }
553 gtk_widget_show_all( button );
554 Gtk::Widget* wrapped = Glib::wrap(button);
555 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
556 holder->add(*wrapped);
558 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
559 return holder;
560 }
562 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
563 {
564 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
565 Gtk::Action::connect_proxy_vfunc(proxy);
566 }
568 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
569 {
570 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
571 Gtk::Action::disconnect_proxy_vfunc(proxy);
572 }
574 void VerbAction::set_active(bool active)
575 {
576 this->active = active;
577 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
578 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
579 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
580 if (ti) {
581 // *should* have one child that is the SPButton
582 Gtk::Widget* child = ti->get_child();
583 if ( child && SP_IS_BUTTON(child->gobj()) ) {
584 SPButton* button = SP_BUTTON(child->gobj());
585 sp_button_toggle_set_down( button, active );
586 }
587 }
588 }
589 }
591 void VerbAction::on_activate()
592 {
593 if ( verb ) {
594 SPAction *action = verb->get_action(view);
595 if ( action ) {
596 sp_action_perform(action, 0);
597 }
598 }
599 }
601 /* Global text entry widgets necessary for update */
602 /* GtkWidget *dropper_rgb_entry,
603 *dropper_opacity_entry ; */
604 // should be made a private member once this is converted to class
606 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
607 connection->disconnect();
608 delete connection;
609 }
611 static void purge_repr_listener( GObject* obj, GObject* tbl )
612 {
613 (void)obj;
614 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
615 if (oldrepr) { // remove old listener
616 sp_repr_remove_listener_by_data(oldrepr, tbl);
617 Inkscape::GC::release(oldrepr);
618 oldrepr = 0;
619 g_object_set_data( tbl, "repr", NULL );
620 }
621 }
623 GtkWidget *
624 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
625 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
626 Inkscape::UI::View::View *view, GtkTooltips *tt)
627 {
628 SPAction *action = verb->get_action(view);
629 if (!action) return NULL;
631 SPAction *doubleclick_action;
632 if (doubleclick_verb)
633 doubleclick_action = doubleclick_verb->get_action(view);
634 else
635 doubleclick_action = NULL;
637 /* fixme: Handle sensitive/unsensitive */
638 /* fixme: Implement sp_button_new_from_action */
639 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
640 gtk_widget_show(b);
643 unsigned int shortcut = sp_shortcut_get_primary(verb);
644 if (shortcut) {
645 gchar key[256];
646 sp_ui_shortcut_string(shortcut, key);
647 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
648 if ( t ) {
649 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
650 }
651 g_free(tip);
652 } else {
653 if ( t ) {
654 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
655 }
656 }
658 return b;
659 }
662 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
663 {
664 SPAction* targetAction = SP_ACTION(user_data);
665 if ( targetAction ) {
666 sp_action_perform( targetAction, NULL );
667 }
668 }
670 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
671 {
672 if ( data ) {
673 GtkAction* act = GTK_ACTION(data);
674 gtk_action_set_sensitive( act, sensitive );
675 }
676 }
678 static SPActionEventVector action_event_vector = {
679 {NULL},
680 NULL,
681 NULL,
682 sp_action_action_set_sensitive,
683 NULL,
684 NULL
685 };
687 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
688 {
689 GtkAction* act = 0;
691 SPAction* targetAction = verb->get_action(view);
692 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
693 act = GTK_ACTION(inky);
694 gtk_action_set_sensitive( act, targetAction->sensitive );
696 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
698 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
699 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
701 return act;
702 }
704 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
705 {
706 Inkscape::UI::View::View *view = desktop;
707 gint verbsToUse[] = {
708 // disabled until we have icons for them:
709 //find
710 //SP_VERB_EDIT_TILE,
711 //SP_VERB_EDIT_UNTILE,
712 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
713 SP_VERB_DIALOG_DISPLAY,
714 SP_VERB_DIALOG_FILL_STROKE,
715 SP_VERB_DIALOG_NAMEDVIEW,
716 SP_VERB_DIALOG_TEXT,
717 SP_VERB_DIALOG_XML_EDITOR,
718 SP_VERB_EDIT_CLONE,
719 SP_VERB_EDIT_COPY,
720 SP_VERB_EDIT_CUT,
721 SP_VERB_EDIT_DUPLICATE,
722 SP_VERB_EDIT_PASTE,
723 SP_VERB_EDIT_REDO,
724 SP_VERB_EDIT_UNDO,
725 SP_VERB_EDIT_UNLINK_CLONE,
726 SP_VERB_FILE_EXPORT,
727 SP_VERB_FILE_IMPORT,
728 SP_VERB_FILE_NEW,
729 SP_VERB_FILE_OPEN,
730 SP_VERB_FILE_PRINT,
731 SP_VERB_FILE_SAVE,
732 SP_VERB_OBJECT_TO_CURVE,
733 SP_VERB_SELECTION_GROUP,
734 SP_VERB_SELECTION_OUTLINE,
735 SP_VERB_SELECTION_UNGROUP,
736 SP_VERB_ZOOM_1_1,
737 SP_VERB_ZOOM_1_2,
738 SP_VERB_ZOOM_2_1,
739 SP_VERB_ZOOM_DRAWING,
740 SP_VERB_ZOOM_IN,
741 SP_VERB_ZOOM_NEXT,
742 SP_VERB_ZOOM_OUT,
743 SP_VERB_ZOOM_PAGE,
744 SP_VERB_ZOOM_PAGE_WIDTH,
745 SP_VERB_ZOOM_PREV,
746 SP_VERB_ZOOM_SELECTION,
747 };
749 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
751 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
752 Glib::RefPtr<Gtk::ActionGroup> mainActions;
753 if ( groups.find(desktop) != groups.end() ) {
754 mainActions = groups[desktop];
755 }
757 if ( !mainActions ) {
758 mainActions = Gtk::ActionGroup::create("main");
759 groups[desktop] = mainActions;
760 }
762 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
763 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
764 if ( verb ) {
765 if (!mainActions->get_action(verb->get_id())) {
766 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
767 mainActions->add(Glib::wrap(act));
768 }
769 }
770 }
772 if ( !mainActions->get_action("ToolZoom") ) {
773 GtkTooltips *tt = gtk_tooltips_new();
774 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
775 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
776 if ( va ) {
777 mainActions->add(va);
778 if ( i == 0 ) {
779 va->set_active(true);
780 }
781 }
782 }
783 }
786 return mainActions;
787 }
790 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
791 {
792 gtk_widget_set_size_request( widget,
793 widget->allocation.width,
794 widget->allocation.height );
795 }
797 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
798 {
799 gtk_widget_set_size_request( widget, -1, -1 );
800 }
804 GtkWidget *
805 sp_tool_toolbox_new()
806 {
807 GtkTooltips *tt = gtk_tooltips_new();
808 GtkWidget* tb = gtk_toolbar_new();
809 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
810 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
812 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
813 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
815 gtk_widget_set_sensitive(tb, FALSE);
817 GtkWidget *hb = gtk_handle_box_new();
818 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
819 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
820 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
822 gtk_container_add(GTK_CONTAINER(hb), tb);
823 gtk_widget_show(GTK_WIDGET(tb));
825 sigc::connection* conn = new sigc::connection;
826 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
828 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
829 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
831 return hb;
832 }
834 GtkWidget *
835 sp_aux_toolbox_new()
836 {
837 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
839 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
841 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
843 gtk_widget_set_sensitive(tb, FALSE);
845 GtkWidget *hb = gtk_handle_box_new();
846 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
847 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
848 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
850 gtk_container_add(GTK_CONTAINER(hb), tb);
851 gtk_widget_show(GTK_WIDGET(tb));
853 sigc::connection* conn = new sigc::connection;
854 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
856 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
857 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
859 return hb;
860 }
862 //####################################
863 //# Commands Bar
864 //####################################
866 GtkWidget *
867 sp_commands_toolbox_new()
868 {
869 GtkWidget *tb = gtk_toolbar_new();
871 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
872 gtk_widget_set_sensitive(tb, FALSE);
874 GtkWidget *hb = gtk_handle_box_new();
875 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
876 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
877 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
879 gtk_container_add(GTK_CONTAINER(hb), tb);
880 gtk_widget_show(GTK_WIDGET(tb));
882 sigc::connection* conn = new sigc::connection;
883 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
885 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
886 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
888 return hb;
889 }
892 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
893 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
894 gchar const *path, gchar const *data, gdouble def,
895 GtkWidget *focusTarget,
896 GtkWidget *us,
897 GObject *dataKludge,
898 gboolean altx, gchar const *altx_mark,
899 gdouble lower, gdouble upper, gdouble step, gdouble page,
900 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
901 void (*callback)(GtkAdjustment *, GObject *),
902 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
903 {
904 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
905 lower, upper, step, page, page ) );
906 if (us) {
907 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
908 }
910 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
912 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
913 if ( shortLabel ) {
914 g_object_set( act, "short_label", shortLabel, NULL );
915 }
917 if ( (descrCount > 0) && descrLabels && descrValues ) {
918 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
919 }
921 if ( focusTarget ) {
922 ege_adjustment_action_set_focuswidget( act, focusTarget );
923 }
925 if ( altx && altx_mark ) {
926 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
927 }
929 if ( dataKludge ) {
930 g_object_set_data( dataKludge, data, adj );
931 }
933 // Using a cast just to make sure we pass in the right kind of function pointer
934 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
936 return act;
937 }
940 //####################################
941 //# node editing callbacks
942 //####################################
944 /**
945 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
946 */
947 static ShapeEditor *get_current_shape_editor()
948 {
949 if (!SP_ACTIVE_DESKTOP) {
950 return NULL;
951 }
953 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
955 if (!SP_IS_NODE_CONTEXT(event_context)) {
956 return NULL;
957 }
959 return SP_NODE_CONTEXT(event_context)->shape_editor;
960 }
963 void
964 sp_node_path_edit_add(void)
965 {
966 ShapeEditor *shape_editor = get_current_shape_editor();
967 if (shape_editor) shape_editor->add_node();
968 }
970 void
971 sp_node_path_edit_delete(void)
972 {
973 ShapeEditor *shape_editor = get_current_shape_editor();
974 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
975 }
977 void
978 sp_node_path_edit_delete_segment(void)
979 {
980 ShapeEditor *shape_editor = get_current_shape_editor();
981 if (shape_editor) shape_editor->delete_segment();
982 }
984 void
985 sp_node_path_edit_break(void)
986 {
987 ShapeEditor *shape_editor = get_current_shape_editor();
988 if (shape_editor) shape_editor->break_at_nodes();
989 }
991 void
992 sp_node_path_edit_join(void)
993 {
994 ShapeEditor *shape_editor = get_current_shape_editor();
995 if (shape_editor) shape_editor->join_nodes();
996 }
998 void
999 sp_node_path_edit_join_segment(void)
1000 {
1001 ShapeEditor *shape_editor = get_current_shape_editor();
1002 if (shape_editor) shape_editor->join_segments();
1003 }
1005 void
1006 sp_node_path_edit_toline(void)
1007 {
1008 ShapeEditor *shape_editor = get_current_shape_editor();
1009 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1010 }
1012 void
1013 sp_node_path_edit_tocurve(void)
1014 {
1015 ShapeEditor *shape_editor = get_current_shape_editor();
1016 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1017 }
1019 void
1020 sp_node_path_edit_cusp(void)
1021 {
1022 ShapeEditor *shape_editor = get_current_shape_editor();
1023 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1024 }
1026 void
1027 sp_node_path_edit_smooth(void)
1028 {
1029 ShapeEditor *shape_editor = get_current_shape_editor();
1030 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1031 }
1033 void
1034 sp_node_path_edit_symmetrical(void)
1035 {
1036 ShapeEditor *shape_editor = get_current_shape_editor();
1037 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1038 }
1040 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1041 bool show = gtk_toggle_action_get_active( act );
1042 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1043 ShapeEditor *shape_editor = get_current_shape_editor();
1044 if (shape_editor) shape_editor->show_handles(show);
1045 }
1047 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1048 bool show = gtk_toggle_action_get_active( act );
1049 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1050 ShapeEditor *shape_editor = get_current_shape_editor();
1051 if (shape_editor) shape_editor->show_helperpath(show);
1052 }
1054 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1055 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1056 }
1058 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1059 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1060 }
1062 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1063 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1064 }
1066 /* is called when the node selection is modified */
1067 static void
1068 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1069 {
1070 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1071 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1072 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1073 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1075 // quit if run by the attr_changed listener
1076 if (g_object_get_data( tbl, "freeze" )) {
1077 return;
1078 }
1080 // in turn, prevent listener from responding
1081 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1083 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1084 SPUnit const *unit = tracker->getActiveUnit();
1086 ShapeEditor *shape_editor = get_current_shape_editor();
1087 if (shape_editor && shape_editor->has_nodepath()) {
1088 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1089 int n_selected = 0;
1090 if (nodepath) {
1091 n_selected = nodepath->numSelected();
1092 }
1094 if (n_selected == 0) {
1095 gtk_action_set_sensitive(xact, FALSE);
1096 gtk_action_set_sensitive(yact, FALSE);
1097 } else {
1098 gtk_action_set_sensitive(xact, TRUE);
1099 gtk_action_set_sensitive(yact, TRUE);
1100 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1101 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1103 if (n_selected == 1) {
1104 NR::Point sel_node = nodepath->singleSelectedCoords();
1105 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1106 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1107 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1108 }
1109 } else {
1110 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1111 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1112 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1113 /* Note: Currently x and y will always have a value, even if the coordinates of the
1114 selected nodes don't coincide (in this case we use the coordinates of the center
1115 of the bounding box). So the entries are never set to zero. */
1116 // FIXME: Maybe we should clear the entry if several nodes are selected
1117 // instead of providing a kind of average value
1118 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1119 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1120 }
1121 }
1122 }
1123 } else {
1124 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1125 gtk_action_set_sensitive(xact, FALSE);
1126 gtk_action_set_sensitive(yact, FALSE);
1127 }
1129 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1130 }
1132 static void
1133 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1134 {
1135 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1137 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1138 SPUnit const *unit = tracker->getActiveUnit();
1140 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1141 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1142 }
1144 // quit if run by the attr_changed listener
1145 if (g_object_get_data( tbl, "freeze" )) {
1146 return;
1147 }
1149 // in turn, prevent listener from responding
1150 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1152 ShapeEditor *shape_editor = get_current_shape_editor();
1153 if (shape_editor && shape_editor->has_nodepath()) {
1154 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1155 if (!strcmp(value_name, "x")) {
1156 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1157 }
1158 if (!strcmp(value_name, "y")) {
1159 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1160 }
1161 }
1163 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1164 }
1166 static void
1167 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1168 {
1169 sp_node_path_value_changed(adj, tbl, "x");
1170 }
1172 static void
1173 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1174 {
1175 sp_node_path_value_changed(adj, tbl, "y");
1176 }
1178 void
1179 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1180 {
1181 {
1182 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1183 SPItem *item = selection->singleItem();
1184 if (item && SP_IS_LPE_ITEM(item)) {
1185 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1186 gtk_action_set_sensitive(w, TRUE);
1187 } else {
1188 gtk_action_set_sensitive(w, FALSE);
1189 }
1190 } else {
1191 gtk_action_set_sensitive(w, FALSE);
1192 }
1193 }
1195 {
1196 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1197 SPItem *item = selection->singleItem();
1198 if (item && item->clip_ref && item->clip_ref->getObject()) {
1199 gtk_action_set_sensitive(w, TRUE);
1200 } else {
1201 gtk_action_set_sensitive(w, FALSE);
1202 }
1203 }
1205 {
1206 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1207 SPItem *item = selection->singleItem();
1208 if (item && item->mask_ref && item->mask_ref->getObject()) {
1209 gtk_action_set_sensitive(w, TRUE);
1210 } else {
1211 gtk_action_set_sensitive(w, FALSE);
1212 }
1213 }
1214 }
1216 void
1217 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1218 {
1219 sp_node_toolbox_sel_changed (selection, tbl);
1220 }
1224 //################################
1225 //## Node Editing Toolbox ##
1226 //################################
1228 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1229 {
1230 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1231 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1232 g_object_set_data( holder, "tracker", tracker );
1234 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1236 {
1237 InkAction* inky = ink_action_new( "NodeInsertAction",
1238 _("Insert node"),
1239 _("Insert new nodes into selected segments"),
1240 "node_insert",
1241 secondarySize );
1242 g_object_set( inky, "short_label", _("Insert"), NULL );
1243 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1244 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1245 }
1247 {
1248 InkAction* inky = ink_action_new( "NodeDeleteAction",
1249 _("Delete node"),
1250 _("Delete selected nodes"),
1251 "node_delete",
1252 secondarySize );
1253 g_object_set( inky, "short_label", _("Delete"), NULL );
1254 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1255 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1256 }
1258 {
1259 InkAction* inky = ink_action_new( "NodeJoinAction",
1260 _("Join endnodes"),
1261 _("Join selected endnodes"),
1262 "node_join",
1263 secondarySize );
1264 g_object_set( inky, "short_label", _("Join"), NULL );
1265 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1266 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1267 }
1269 {
1270 InkAction* inky = ink_action_new( "NodeBreakAction",
1271 _("Break nodes"),
1272 _("Break path at selected nodes"),
1273 "node_break",
1274 secondarySize );
1275 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1276 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1277 }
1280 {
1281 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1282 _("Join with segment"),
1283 _("Join selected endnodes with a new segment"),
1284 "node_join_segment",
1285 secondarySize );
1286 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1287 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1288 }
1290 {
1291 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1292 _("Delete segment"),
1293 _("Delete segment between two non-endpoint nodes"),
1294 "node_delete_segment",
1295 secondarySize );
1296 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1297 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1298 }
1300 {
1301 InkAction* inky = ink_action_new( "NodeCuspAction",
1302 _("Node Cusp"),
1303 _("Make selected nodes corner"),
1304 "node_cusp",
1305 secondarySize );
1306 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1307 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1308 }
1310 {
1311 InkAction* inky = ink_action_new( "NodeSmoothAction",
1312 _("Node Smooth"),
1313 _("Make selected nodes smooth"),
1314 "node_smooth",
1315 secondarySize );
1316 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1317 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1318 }
1320 {
1321 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1322 _("Node Symmetric"),
1323 _("Make selected nodes symmetric"),
1324 "node_symmetric",
1325 secondarySize );
1326 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1327 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328 }
1330 {
1331 InkAction* inky = ink_action_new( "NodeLineAction",
1332 _("Node Line"),
1333 _("Make selected segments lines"),
1334 "node_line",
1335 secondarySize );
1336 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1337 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338 }
1340 {
1341 InkAction* inky = ink_action_new( "NodeCurveAction",
1342 _("Node Curve"),
1343 _("Make selected segments curves"),
1344 "node_curve",
1345 secondarySize );
1346 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1347 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348 }
1350 {
1351 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1352 _("Show Handles"),
1353 _("Show the Bezier handles of selected nodes"),
1354 "nodes_show_handles",
1355 Inkscape::ICON_SIZE_DECORATION );
1356 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1357 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1358 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1359 }
1361 {
1362 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1363 _("Show Outline"),
1364 _("Show the outline of the path"),
1365 "nodes_show_helperpath",
1366 Inkscape::ICON_SIZE_DECORATION );
1367 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1368 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1369 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1370 }
1372 {
1373 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1374 _("Next path effect parameter"),
1375 _("Show next path effect parameter for editing"),
1376 "edit_next_parameter",
1377 Inkscape::ICON_SIZE_DECORATION );
1378 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1379 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1380 g_object_set_data( holder, "nodes_lpeedit", inky);
1381 }
1383 {
1384 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1385 _("Edit clipping path"),
1386 _("Edit the clipping path of the object"),
1387 "nodeedit-clippath",
1388 Inkscape::ICON_SIZE_DECORATION );
1389 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1390 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1391 g_object_set_data( holder, "nodes_clippathedit", inky);
1392 }
1394 {
1395 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1396 _("Edit mask path"),
1397 _("Edit the mask of the object"),
1398 "nodeedit-mask",
1399 Inkscape::ICON_SIZE_DECORATION );
1400 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1401 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402 g_object_set_data( holder, "nodes_maskedit", inky);
1403 }
1405 /* X coord of selected node(s) */
1406 {
1407 EgeAdjustmentAction* eact = 0;
1408 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1409 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1410 eact = create_adjustment_action( "NodeXAction",
1411 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1412 "tools.nodes", "Xcoord", 0,
1413 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1414 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1415 labels, values, G_N_ELEMENTS(labels),
1416 sp_node_path_x_value_changed );
1417 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1418 g_object_set_data( holder, "nodes_x_action", eact );
1419 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1420 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1421 }
1423 /* Y coord of selected node(s) */
1424 {
1425 EgeAdjustmentAction* eact = 0;
1426 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1427 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1428 eact = create_adjustment_action( "NodeYAction",
1429 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1430 "tools.nodes", "Ycoord", 0,
1431 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1432 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1433 labels, values, G_N_ELEMENTS(labels),
1434 sp_node_path_y_value_changed );
1435 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1436 g_object_set_data( holder, "nodes_y_action", eact );
1437 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1438 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1439 }
1441 // add the units menu
1442 {
1443 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1444 gtk_action_group_add_action( mainActions, act );
1445 }
1448 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1450 //watch selection
1451 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1453 sigc::connection *c_selection_changed =
1454 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1455 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1456 pool->add_connection ("selection-changed", c_selection_changed);
1458 sigc::connection *c_selection_modified =
1459 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1460 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1461 pool->add_connection ("selection-modified", c_selection_modified);
1463 sigc::connection *c_subselection_changed =
1464 new sigc::connection (desktop->connectToolSubselectionChanged
1465 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1466 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1468 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1470 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1471 } // end of sp_node_toolbox_prep()
1474 //########################
1475 //## Zoom Toolbox ##
1476 //########################
1478 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1479 {
1480 // no custom GtkAction setup needed
1481 } // end of sp_zoom_toolbox_prep()
1483 void
1484 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1485 {
1486 toolbox_set_desktop(toolbox,
1487 desktop,
1488 setup_tool_toolbox,
1489 update_tool_toolbox,
1490 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1491 "event_context_connection")));
1492 }
1495 void
1496 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1497 {
1498 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1499 desktop,
1500 setup_aux_toolbox,
1501 update_aux_toolbox,
1502 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1503 "event_context_connection")));
1504 }
1506 void
1507 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1508 {
1509 toolbox_set_desktop(toolbox,
1510 desktop,
1511 setup_commands_toolbox,
1512 update_commands_toolbox,
1513 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1514 "event_context_connection")));
1515 }
1517 static void
1518 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1519 {
1520 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1521 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1523 if (old_desktop) {
1524 GList *children, *iter;
1526 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1527 for ( iter = children ; iter ; iter = iter->next ) {
1528 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1529 }
1530 g_list_free(children);
1531 }
1533 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1535 if (desktop) {
1536 gtk_widget_set_sensitive(toolbox, TRUE);
1537 setup_func(toolbox, desktop);
1538 update_func(desktop, desktop->event_context, toolbox);
1539 *conn = desktop->connectEventContextChanged
1540 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1541 } else {
1542 gtk_widget_set_sensitive(toolbox, FALSE);
1543 }
1545 } // end of toolbox_set_desktop()
1548 static void
1549 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1550 {
1551 gchar const * descr =
1552 "<ui>"
1553 " <toolbar name='ToolToolbar'>"
1554 " <toolitem action='ToolSelector' />"
1555 " <toolitem action='ToolNode' />"
1556 " <toolitem action='ToolTweak' />"
1557 " <toolitem action='ToolZoom' />"
1558 " <toolitem action='ToolRect' />"
1559 " <toolitem action='Tool3DBox' />"
1560 " <toolitem action='ToolArc' />"
1561 " <toolitem action='ToolStar' />"
1562 " <toolitem action='ToolSpiral' />"
1563 " <toolitem action='ToolPencil' />"
1564 " <toolitem action='ToolPen' />"
1565 " <toolitem action='ToolCalligraphic' />"
1566 " <toolitem action='ToolEraser' />"
1567 " <toolitem action='ToolPaintBucket' />"
1568 " <toolitem action='ToolText' />"
1569 " <toolitem action='ToolConnector' />"
1570 " <toolitem action='ToolGradient' />"
1571 " <toolitem action='ToolDropper' />"
1572 " </toolbar>"
1573 "</ui>";
1574 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1575 GtkUIManager* mgr = gtk_ui_manager_new();
1576 GError* errVal = 0;
1578 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1579 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1581 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1582 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1583 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1584 }
1585 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1586 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1588 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1589 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1591 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1593 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1594 if ( child ) {
1595 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1596 }
1598 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1599 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1600 }
1603 static void
1604 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1605 {
1606 gchar const *const tname = ( eventcontext
1607 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1608 : NULL );
1609 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1611 for (int i = 0 ; tools[i].type_name ; i++ ) {
1612 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1613 if ( act ) {
1614 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1615 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1616 if ( verbAct ) {
1617 verbAct->set_active(setActive);
1618 }
1619 }
1620 }
1621 }
1623 static void
1624 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1625 {
1626 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1627 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1628 GtkUIManager* mgr = gtk_ui_manager_new();
1629 GError* errVal = 0;
1630 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1631 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1633 std::map<std::string, GtkWidget*> dataHolders;
1635 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1636 if ( aux_toolboxes[i].prep_func ) {
1637 // converted to GtkActions and UIManager
1639 GtkWidget* kludge = gtk_toolbar_new();
1640 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1641 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1642 dataHolders[aux_toolboxes[i].type_name] = kludge;
1643 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1644 } else {
1646 GtkWidget *sub_toolbox = 0;
1647 if (aux_toolboxes[i].create_func == NULL)
1648 sub_toolbox = sp_empty_toolbox_new(desktop);
1649 else {
1650 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1651 }
1653 gtk_size_group_add_widget( grouper, sub_toolbox );
1655 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1656 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1658 }
1659 }
1661 // Second pass to create toolbars *after* all GtkActions are created
1662 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1663 if ( aux_toolboxes[i].prep_func ) {
1664 // converted to GtkActions and UIManager
1666 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1668 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1669 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1671 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1672 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1673 g_free( tmp );
1674 tmp = 0;
1676 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1677 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1678 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1679 }
1680 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1683 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1685 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1686 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1687 swatch->setDesktop( desktop );
1688 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1689 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1690 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1691 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 );
1692 }
1694 gtk_widget_show_all( holder );
1695 sp_set_font_size_smaller( holder );
1697 gtk_size_group_add_widget( grouper, holder );
1699 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1700 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1701 }
1702 }
1704 g_object_unref( G_OBJECT(grouper) );
1705 }
1707 static void
1708 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1709 {
1710 gchar const *tname = ( eventcontext
1711 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1712 : NULL );
1713 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1714 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1715 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1716 gtk_widget_show_all(sub_toolbox);
1717 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1718 } else {
1719 gtk_widget_hide(sub_toolbox);
1720 }
1721 }
1722 }
1724 static void
1725 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1726 {
1727 gchar const * descr =
1728 "<ui>"
1729 " <toolbar name='CommandsToolbar'>"
1730 " <toolitem action='FileNew' />"
1731 " <toolitem action='FileOpen' />"
1732 " <toolitem action='FileSave' />"
1733 " <toolitem action='FilePrint' />"
1734 " <separator />"
1735 " <toolitem action='FileImport' />"
1736 " <toolitem action='FileExport' />"
1737 " <separator />"
1738 " <toolitem action='EditUndo' />"
1739 " <toolitem action='EditRedo' />"
1740 " <separator />"
1741 " <toolitem action='EditCopy' />"
1742 " <toolitem action='EditCut' />"
1743 " <toolitem action='EditPaste' />"
1744 " <separator />"
1745 " <toolitem action='ZoomSelection' />"
1746 " <toolitem action='ZoomDrawing' />"
1747 " <toolitem action='ZoomPage' />"
1748 " <separator />"
1749 " <toolitem action='EditDuplicate' />"
1750 " <toolitem action='EditClone' />"
1751 " <toolitem action='EditUnlinkClone' />"
1752 " <separator />"
1753 " <toolitem action='SelectionGroup' />"
1754 " <toolitem action='SelectionUnGroup' />"
1755 " <separator />"
1756 " <toolitem action='DialogFillStroke' />"
1757 " <toolitem action='DialogText' />"
1758 " <toolitem action='DialogXMLEditor' />"
1759 " <toolitem action='DialogAlignDistribute' />"
1760 " <separator />"
1761 " <toolitem action='DialogPreferences' />"
1762 " <toolitem action='DialogDocumentProperties' />"
1763 " </toolbar>"
1764 "</ui>";
1765 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1768 GtkUIManager* mgr = gtk_ui_manager_new();
1769 GError* errVal = 0;
1771 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1772 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1774 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1775 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1776 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1777 }
1779 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1780 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1782 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1783 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1786 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1788 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1789 if ( child ) {
1790 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1791 }
1793 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1794 }
1796 static void
1797 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1798 {
1799 }
1801 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1802 {
1803 gtk_widget_show(toolbox_toplevel);
1804 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1806 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1807 if (!shown_toolbox) {
1808 return;
1809 }
1810 gtk_widget_show(toolbox);
1812 gtk_widget_show_all(shown_toolbox);
1813 }
1815 static GtkWidget *
1816 sp_empty_toolbox_new(SPDesktop *desktop)
1817 {
1818 GtkWidget *tbl = gtk_toolbar_new();
1819 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1820 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1822 gtk_widget_show_all(tbl);
1823 sp_set_font_size_smaller (tbl);
1825 return tbl;
1826 }
1828 #define MODE_LABEL_WIDTH 70
1830 //########################
1831 //## Star ##
1832 //########################
1834 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1835 {
1836 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1838 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1839 // do not remember prefs if this call is initiated by an undo change, because undoing object
1840 // creation sets bogus values to its attributes before it is deleted
1841 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1842 }
1844 // quit if run by the attr_changed listener
1845 if (g_object_get_data( dataKludge, "freeze" )) {
1846 return;
1847 }
1849 // in turn, prevent listener from responding
1850 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1852 bool modmade = false;
1854 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1855 GSList const *items = selection->itemList();
1856 for (; items != NULL; items = items->next) {
1857 if (SP_IS_STAR((SPItem *) items->data)) {
1858 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1859 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1860 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1861 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1862 + M_PI / (gint)adj->value));
1863 SP_OBJECT((SPItem *) items->data)->updateRepr();
1864 modmade = true;
1865 }
1866 }
1867 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1868 _("Star: Change number of corners"));
1870 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1871 }
1873 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1874 {
1875 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1877 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1878 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1879 }
1881 // quit if run by the attr_changed listener
1882 if (g_object_get_data( dataKludge, "freeze" )) {
1883 return;
1884 }
1886 // in turn, prevent listener from responding
1887 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1889 bool modmade = false;
1890 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1891 GSList const *items = selection->itemList();
1892 for (; items != NULL; items = items->next) {
1893 if (SP_IS_STAR((SPItem *) items->data)) {
1894 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1896 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1897 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1898 if (r2 < r1) {
1899 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1900 } else {
1901 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1902 }
1904 SP_OBJECT((SPItem *) items->data)->updateRepr();
1905 modmade = true;
1906 }
1907 }
1909 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1910 _("Star: Change spoke ratio"));
1912 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1913 }
1915 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1916 {
1917 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1918 bool flat = ege_select_one_action_get_active( act ) == 0;
1920 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1921 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1922 flat ? "true" : "false" );
1923 }
1925 // quit if run by the attr_changed listener
1926 if (g_object_get_data( dataKludge, "freeze" )) {
1927 return;
1928 }
1930 // in turn, prevent listener from responding
1931 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1933 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1934 GSList const *items = selection->itemList();
1935 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1936 bool modmade = false;
1938 if ( prop_action ) {
1939 gtk_action_set_sensitive( prop_action, !flat );
1940 }
1942 for (; items != NULL; items = items->next) {
1943 if (SP_IS_STAR((SPItem *) items->data)) {
1944 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1945 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1946 SP_OBJECT((SPItem *) items->data)->updateRepr();
1947 modmade = true;
1948 }
1949 }
1951 if (modmade) {
1952 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1953 flat ? _("Make polygon") : _("Make star"));
1954 }
1956 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1957 }
1959 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1960 {
1961 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1963 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1964 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1965 }
1967 // quit if run by the attr_changed listener
1968 if (g_object_get_data( dataKludge, "freeze" )) {
1969 return;
1970 }
1972 // in turn, prevent listener from responding
1973 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1975 bool modmade = false;
1977 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1978 GSList const *items = selection->itemList();
1979 for (; items != NULL; items = items->next) {
1980 if (SP_IS_STAR((SPItem *) items->data)) {
1981 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1982 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1983 SP_OBJECT(items->data)->updateRepr();
1984 modmade = true;
1985 }
1986 }
1987 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1988 _("Star: Change rounding"));
1990 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1991 }
1993 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1994 {
1995 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1997 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1998 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1999 }
2001 // quit if run by the attr_changed listener
2002 if (g_object_get_data( dataKludge, "freeze" )) {
2003 return;
2004 }
2006 // in turn, prevent listener from responding
2007 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2009 bool modmade = false;
2011 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2012 GSList const *items = selection->itemList();
2013 for (; items != NULL; items = items->next) {
2014 if (SP_IS_STAR((SPItem *) items->data)) {
2015 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2016 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2017 SP_OBJECT(items->data)->updateRepr();
2018 modmade = true;
2019 }
2020 }
2021 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2022 _("Star: Change randomization"));
2024 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2025 }
2028 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2029 gchar const */*old_value*/, gchar const */*new_value*/,
2030 bool /*is_interactive*/, gpointer data)
2031 {
2032 GtkWidget *tbl = GTK_WIDGET(data);
2034 // quit if run by the _changed callbacks
2035 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2036 return;
2037 }
2039 // in turn, prevent callbacks from responding
2040 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2042 GtkAdjustment *adj = 0;
2044 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2045 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2047 if (!strcmp(name, "inkscape:randomized")) {
2048 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2049 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2050 } else if (!strcmp(name, "inkscape:rounded")) {
2051 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2052 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2053 } else if (!strcmp(name, "inkscape:flatsided")) {
2054 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2055 char const *flatsides = repr->attribute("inkscape:flatsided");
2056 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2057 if ( flatsides && !strcmp(flatsides,"false") ) {
2058 ege_select_one_action_set_active( flat_action, 1 );
2059 gtk_action_set_sensitive( prop_action, TRUE );
2060 } else {
2061 ege_select_one_action_set_active( flat_action, 0 );
2062 gtk_action_set_sensitive( prop_action, FALSE );
2063 }
2064 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2065 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2066 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2067 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2068 if (r2 < r1) {
2069 gtk_adjustment_set_value(adj, r2/r1);
2070 } else {
2071 gtk_adjustment_set_value(adj, r1/r2);
2072 }
2073 } else if (!strcmp(name, "sodipodi:sides")) {
2074 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2075 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2076 }
2078 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2079 }
2082 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2083 {
2084 NULL, /* child_added */
2085 NULL, /* child_removed */
2086 star_tb_event_attr_changed,
2087 NULL, /* content_changed */
2088 NULL /* order_changed */
2089 };
2092 /**
2093 * \param selection Should not be NULL.
2094 */
2095 static void
2096 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2097 {
2098 int n_selected = 0;
2099 Inkscape::XML::Node *repr = NULL;
2101 purge_repr_listener( tbl, tbl );
2103 for (GSList const *items = selection->itemList();
2104 items != NULL;
2105 items = items->next)
2106 {
2107 if (SP_IS_STAR((SPItem *) items->data)) {
2108 n_selected++;
2109 repr = SP_OBJECT_REPR((SPItem *) items->data);
2110 }
2111 }
2113 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2115 if (n_selected == 0) {
2116 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2117 } else if (n_selected == 1) {
2118 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2120 if (repr) {
2121 g_object_set_data( tbl, "repr", repr );
2122 Inkscape::GC::anchor(repr);
2123 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2124 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2125 }
2126 } else {
2127 // FIXME: implement averaging of all parameters for multiple selected stars
2128 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2129 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2130 }
2131 }
2134 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2135 {
2136 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2137 // callbacks to lump all the changes for all selected objects in one undo step
2139 GtkAdjustment *adj = 0;
2141 // fixme: make settable in prefs!
2142 gint mag = 5;
2143 gdouble prop = 0.5;
2144 gboolean flat = FALSE;
2145 gdouble randomized = 0;
2146 gdouble rounded = 0;
2148 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2149 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2151 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2152 gtk_action_set_sensitive( sb2, !flat );
2154 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2155 gtk_adjustment_set_value(adj, mag);
2156 gtk_adjustment_value_changed(adj);
2158 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2159 gtk_adjustment_set_value(adj, prop);
2160 gtk_adjustment_value_changed(adj);
2162 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2163 gtk_adjustment_set_value(adj, rounded);
2164 gtk_adjustment_value_changed(adj);
2166 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2167 gtk_adjustment_set_value(adj, randomized);
2168 gtk_adjustment_value_changed(adj);
2169 }
2172 void
2173 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2174 {
2175 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2176 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2177 GtkWidget *l = gtk_label_new(NULL);
2178 gtk_label_set_markup(GTK_LABEL(l), title);
2179 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2180 if ( GTK_IS_TOOLBAR(tbl) ) {
2181 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2182 } else {
2183 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2184 }
2185 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2186 }
2189 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2190 {
2191 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2193 {
2194 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2195 ege_output_action_set_use_markup( act, TRUE );
2196 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2197 g_object_set_data( holder, "mode_action", act );
2198 }
2200 {
2201 EgeAdjustmentAction* eact = 0;
2202 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2203 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2205 /* Flatsided checkbox */
2206 {
2207 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2209 GtkTreeIter iter;
2210 gtk_list_store_append( model, &iter );
2211 gtk_list_store_set( model, &iter,
2212 0, _("Polygon"),
2213 1, _("Regular polygon (with one handle) instead of a star"),
2214 2, "star_flat",
2215 -1 );
2217 gtk_list_store_append( model, &iter );
2218 gtk_list_store_set( model, &iter,
2219 0, _("Star"),
2220 1, _("Star instead of a regular polygon (with one handle)"),
2221 2, "star_angled",
2222 -1 );
2224 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2225 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2226 g_object_set_data( holder, "flat_action", act );
2228 ege_select_one_action_set_appearance( act, "full" );
2229 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2230 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2231 ege_select_one_action_set_icon_column( act, 2 );
2232 ege_select_one_action_set_icon_size( act, secondarySize );
2233 ege_select_one_action_set_tooltip_column( act, 1 );
2235 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2236 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2237 }
2239 /* Magnitude */
2240 {
2241 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2242 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2243 eact = create_adjustment_action( "MagnitudeAction",
2244 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2245 "tools.shapes.star", "magnitude", 3,
2246 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2247 3, 1024, 1, 5,
2248 labels, values, G_N_ELEMENTS(labels),
2249 sp_stb_magnitude_value_changed,
2250 1.0, 0 );
2251 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2252 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2253 }
2255 /* Spoke ratio */
2256 {
2257 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2258 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2259 eact = create_adjustment_action( "SpokeAction",
2260 _("Spoke ratio"), _("Spoke ratio:"),
2261 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2262 // Base radius is the same for the closest handle.
2263 _("Base radius to tip radius ratio"),
2264 "tools.shapes.star", "proportion", 0.5,
2265 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2266 0.01, 1.0, 0.01, 0.1,
2267 labels, values, G_N_ELEMENTS(labels),
2268 sp_stb_proportion_value_changed );
2269 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2270 g_object_set_data( holder, "prop_action", eact );
2271 }
2273 if ( !isFlatSided ) {
2274 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2275 } else {
2276 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2277 }
2279 /* Roundedness */
2280 {
2281 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2282 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2283 eact = create_adjustment_action( "RoundednessAction",
2284 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2285 "tools.shapes.star", "rounded", 0.0,
2286 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2287 -10.0, 10.0, 0.01, 0.1,
2288 labels, values, G_N_ELEMENTS(labels),
2289 sp_stb_rounded_value_changed );
2290 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2291 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2292 }
2294 /* Randomization */
2295 {
2296 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2297 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2298 eact = create_adjustment_action( "RandomizationAction",
2299 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2300 "tools.shapes.star", "randomized", 0.0,
2301 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2302 -10.0, 10.0, 0.001, 0.01,
2303 labels, values, G_N_ELEMENTS(labels),
2304 sp_stb_randomized_value_changed, 0.1, 3 );
2305 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2306 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2307 }
2308 }
2310 {
2311 /* Reset */
2312 {
2313 GtkAction* act = gtk_action_new( "StarResetAction",
2314 _("Defaults"),
2315 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2316 GTK_STOCK_CLEAR );
2317 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2318 gtk_action_group_add_action( mainActions, act );
2319 gtk_action_set_sensitive( act, TRUE );
2320 }
2321 }
2323 sigc::connection *connection = new sigc::connection(
2324 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2325 );
2326 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2327 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2328 }
2331 //########################
2332 //## Rect ##
2333 //########################
2335 static void sp_rtb_sensitivize( GObject *tbl )
2336 {
2337 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2338 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2339 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2341 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2342 gtk_action_set_sensitive( not_rounded, FALSE );
2343 } else {
2344 gtk_action_set_sensitive( not_rounded, TRUE );
2345 }
2346 }
2349 static void
2350 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2351 void (*setter)(SPRect *, gdouble))
2352 {
2353 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2355 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2356 SPUnit const *unit = tracker->getActiveUnit();
2358 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2359 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2360 }
2362 // quit if run by the attr_changed listener
2363 if (g_object_get_data( tbl, "freeze" )) {
2364 return;
2365 }
2367 // in turn, prevent listener from responding
2368 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2370 bool modmade = false;
2371 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2372 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2373 if (SP_IS_RECT(items->data)) {
2374 if (adj->value != 0) {
2375 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2376 } else {
2377 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2378 }
2379 modmade = true;
2380 }
2381 }
2383 sp_rtb_sensitivize( tbl );
2385 if (modmade) {
2386 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2387 _("Change rectangle"));
2388 }
2390 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2391 }
2393 static void
2394 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2395 {
2396 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2397 }
2399 static void
2400 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2401 {
2402 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2403 }
2405 static void
2406 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2407 {
2408 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2409 }
2411 static void
2412 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2413 {
2414 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2415 }
2419 static void
2420 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2421 {
2422 GtkAdjustment *adj = 0;
2424 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2425 gtk_adjustment_set_value(adj, 0.0);
2426 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2427 gtk_adjustment_value_changed(adj);
2429 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2430 gtk_adjustment_set_value(adj, 0.0);
2431 gtk_adjustment_value_changed(adj);
2433 sp_rtb_sensitivize( obj );
2434 }
2436 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2437 gchar const */*old_value*/, gchar const */*new_value*/,
2438 bool /*is_interactive*/, gpointer data)
2439 {
2440 GObject *tbl = G_OBJECT(data);
2442 // quit if run by the _changed callbacks
2443 if (g_object_get_data( tbl, "freeze" )) {
2444 return;
2445 }
2447 // in turn, prevent callbacks from responding
2448 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2450 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2451 SPUnit const *unit = tracker->getActiveUnit();
2453 gpointer item = g_object_get_data( tbl, "item" );
2454 if (item && SP_IS_RECT(item)) {
2455 {
2456 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2457 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2458 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2459 }
2461 {
2462 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2463 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2464 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2465 }
2467 {
2468 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2469 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2470 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2471 }
2473 {
2474 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2475 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2476 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2477 }
2478 }
2480 sp_rtb_sensitivize( tbl );
2482 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2483 }
2486 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2487 NULL, /* child_added */
2488 NULL, /* child_removed */
2489 rect_tb_event_attr_changed,
2490 NULL, /* content_changed */
2491 NULL /* order_changed */
2492 };
2494 /**
2495 * \param selection should not be NULL.
2496 */
2497 static void
2498 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2499 {
2500 int n_selected = 0;
2501 Inkscape::XML::Node *repr = NULL;
2502 SPItem *item = NULL;
2504 if ( g_object_get_data( tbl, "repr" ) ) {
2505 g_object_set_data( tbl, "item", NULL );
2506 }
2507 purge_repr_listener( tbl, tbl );
2509 for (GSList const *items = selection->itemList();
2510 items != NULL;
2511 items = items->next) {
2512 if (SP_IS_RECT((SPItem *) items->data)) {
2513 n_selected++;
2514 item = (SPItem *) items->data;
2515 repr = SP_OBJECT_REPR(item);
2516 }
2517 }
2519 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2521 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2523 if (n_selected == 0) {
2524 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2526 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2527 gtk_action_set_sensitive(w, FALSE);
2528 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2529 gtk_action_set_sensitive(h, FALSE);
2531 } else if (n_selected == 1) {
2532 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2533 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2535 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2536 gtk_action_set_sensitive(w, TRUE);
2537 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2538 gtk_action_set_sensitive(h, TRUE);
2540 if (repr) {
2541 g_object_set_data( tbl, "repr", repr );
2542 g_object_set_data( tbl, "item", item );
2543 Inkscape::GC::anchor(repr);
2544 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2545 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2546 }
2547 } else {
2548 // FIXME: implement averaging of all parameters for multiple selected
2549 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2550 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2551 sp_rtb_sensitivize( tbl );
2552 }
2553 }
2556 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2557 {
2558 EgeAdjustmentAction* eact = 0;
2559 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2561 {
2562 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2563 ege_output_action_set_use_markup( act, TRUE );
2564 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2565 g_object_set_data( holder, "mode_action", act );
2566 }
2568 // rx/ry units menu: create
2569 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2570 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2571 // fixme: add % meaning per cent of the width/height
2572 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2573 g_object_set_data( holder, "tracker", tracker );
2575 /* W */
2576 {
2577 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2578 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2579 eact = create_adjustment_action( "RectWidthAction",
2580 _("Width"), _("W:"), _("Width of rectangle"),
2581 "tools.shapes.rect", "width", 0,
2582 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2583 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2584 labels, values, G_N_ELEMENTS(labels),
2585 sp_rtb_width_value_changed );
2586 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2587 g_object_set_data( holder, "width_action", eact );
2588 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2589 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2590 }
2592 /* H */
2593 {
2594 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2595 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2596 eact = create_adjustment_action( "RectHeightAction",
2597 _("Height"), _("H:"), _("Height of rectangle"),
2598 "tools.shapes.rect", "height", 0,
2599 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2600 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2601 labels, values, G_N_ELEMENTS(labels),
2602 sp_rtb_height_value_changed );
2603 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2604 g_object_set_data( holder, "height_action", eact );
2605 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2606 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2607 }
2609 /* rx */
2610 {
2611 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2612 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2613 eact = create_adjustment_action( "RadiusXAction",
2614 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2615 "tools.shapes.rect", "rx", 0,
2616 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2617 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2618 labels, values, G_N_ELEMENTS(labels),
2619 sp_rtb_rx_value_changed);
2620 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2621 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2622 }
2624 /* ry */
2625 {
2626 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2627 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2628 eact = create_adjustment_action( "RadiusYAction",
2629 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2630 "tools.shapes.rect", "ry", 0,
2631 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2632 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2633 labels, values, G_N_ELEMENTS(labels),
2634 sp_rtb_ry_value_changed);
2635 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2636 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2637 }
2639 // add the units menu
2640 {
2641 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2642 gtk_action_group_add_action( mainActions, act );
2643 }
2645 /* Reset */
2646 {
2647 InkAction* inky = ink_action_new( "RectResetAction",
2648 _("Not rounded"),
2649 _("Make corners sharp"),
2650 "squared_corner",
2651 secondarySize );
2652 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2653 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2654 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2655 g_object_set_data( holder, "not_rounded", inky );
2656 }
2658 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2659 sp_rtb_sensitivize( holder );
2661 sigc::connection *connection = new sigc::connection(
2662 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2663 );
2664 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2665 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2666 }
2668 //########################
2669 //## 3D Box ##
2670 //########################
2672 // normalize angle so that it lies in the interval [0,360]
2673 static double box3d_normalize_angle (double a) {
2674 double angle = a + ((int) (a/360.0))*360;
2675 if (angle < 0) {
2676 angle += 360.0;
2677 }
2678 return angle;
2679 }
2681 static void
2682 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2683 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2684 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2685 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2686 // are reset).
2687 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2689 if (is_infinite) {
2690 gtk_toggle_action_set_active(tact, TRUE);
2691 gtk_action_set_sensitive(act, TRUE);
2693 double angle = persp3d_get_infinite_angle(persp, axis);
2694 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2695 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2696 }
2697 } else {
2698 gtk_toggle_action_set_active(tact, FALSE);
2699 gtk_action_set_sensitive(act, FALSE);
2700 }
2701 }
2703 static void
2704 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2705 if (!persp_repr) {
2706 g_print ("No perspective given to box3d_resync_toolbar().\n");
2707 return;
2708 }
2710 GtkWidget *tbl = GTK_WIDGET(data);
2711 GtkAdjustment *adj = 0;
2712 GtkAction *act = 0;
2713 GtkToggleAction *tact = 0;
2714 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2715 {
2716 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2717 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2718 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2720 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2721 }
2722 {
2723 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2724 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2725 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2727 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2728 }
2729 {
2730 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2731 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2732 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2734 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2735 }
2736 }
2738 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2739 gchar const */*old_value*/, gchar const */*new_value*/,
2740 bool /*is_interactive*/, gpointer data)
2741 {
2742 GtkWidget *tbl = GTK_WIDGET(data);
2744 // quit if run by the attr_changed listener
2745 // note: it used to work without the differently called freeze_ attributes (here and in
2746 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2747 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2748 return;
2749 }
2751 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2752 // sp_document_maybe_done() when the document is undo insensitive)
2753 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2755 // TODO: Only update the appropriate part of the toolbar
2756 // if (!strcmp(name, "inkscape:vp_z")) {
2757 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2758 // }
2760 Persp3D *persp = persp3d_get_from_repr(repr);
2761 persp3d_update_box_reprs(persp);
2763 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2764 }
2766 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2767 {
2768 NULL, /* child_added */
2769 NULL, /* child_removed */
2770 box3d_persp_tb_event_attr_changed,
2771 NULL, /* content_changed */
2772 NULL /* order_changed */
2773 };
2775 /**
2776 * \param selection Should not be NULL.
2777 */
2778 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2779 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2780 static void
2781 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2782 {
2783 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2784 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2785 // update the perspectives with infinite VPs and leave the other ones untouched).
2787 Inkscape::XML::Node *persp_repr = NULL;
2788 purge_repr_listener(tbl, tbl);
2790 SPItem *item = selection->singleItem();
2791 if (item && SP_IS_BOX3D(item)) {
2792 // FIXME: Also deal with multiple selected boxes
2793 SPBox3D *box = SP_BOX3D(item);
2794 Persp3D *persp = box3d_get_perspective(box);
2795 persp_repr = SP_OBJECT_REPR(persp);
2796 if (persp_repr) {
2797 g_object_set_data(tbl, "repr", persp_repr);
2798 Inkscape::GC::anchor(persp_repr);
2799 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2800 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2801 }
2803 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2804 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2806 box3d_resync_toolbar(persp_repr, tbl);
2807 }
2808 }
2810 static void
2811 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2812 {
2813 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2814 SPDocument *document = sp_desktop_document(desktop);
2816 // quit if run by the attr_changed listener
2817 // note: it used to work without the differently called freeze_ attributes (here and in
2818 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2819 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2820 return;
2821 }
2823 // in turn, prevent listener from responding
2824 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2826 //Persp3D *persp = document->current_persp3d;
2827 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2828 if (sel_persps.empty()) {
2829 // this can happen when the document is created; we silently ignore it
2830 return;
2831 }
2832 Persp3D *persp = sel_persps.front();
2834 persp->tmat.set_infinite_direction (axis, adj->value);
2835 SP_OBJECT(persp)->updateRepr();
2837 // TODO: use the correct axis here, too
2838 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2840 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2841 }
2844 static void
2845 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2846 {
2847 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2848 }
2850 static void
2851 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2852 {
2853 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2854 }
2856 static void
2857 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2858 {
2859 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2860 }
2863 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2864 {
2865 // TODO: Take all selected perspectives into account
2866 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2867 if (sel_persps.empty()) {
2868 // this can happen when the document is created; we silently ignore it
2869 return;
2870 }
2871 Persp3D *persp = sel_persps.front();
2873 bool set_infinite = gtk_toggle_action_get_active(act);
2874 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2875 }
2877 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2878 {
2879 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2880 }
2882 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2883 {
2884 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2885 }
2887 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2888 {
2889 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2890 }
2892 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2893 {
2894 EgeAdjustmentAction* eact = 0;
2895 SPDocument *document = sp_desktop_document (desktop);
2896 Persp3D *persp = document->current_persp3d;
2898 EgeAdjustmentAction* box3d_angle_x = 0;
2899 EgeAdjustmentAction* box3d_angle_y = 0;
2900 EgeAdjustmentAction* box3d_angle_z = 0;
2902 /* Angle X */
2903 {
2904 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2905 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2906 eact = create_adjustment_action( "3DBoxAngleXAction",
2907 _("Angle in X direction"), _("Angle X:"),
2908 // Translators: PL is short for 'perspective line'
2909 _("Angle of PLs in X direction"),
2910 "tools.shapes.3dbox", "box3d_angle_x", 30,
2911 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2912 -360.0, 360.0, 1.0, 10.0,
2913 labels, values, G_N_ELEMENTS(labels),
2914 box3d_angle_x_value_changed );
2915 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2916 g_object_set_data( holder, "box3d_angle_x_action", eact );
2917 box3d_angle_x = eact;
2918 }
2920 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2921 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2922 } else {
2923 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2924 }
2927 /* VP X state */
2928 {
2929 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2930 // Translators: VP is short for 'vanishing point'
2931 _("State of VP in X direction"),
2932 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2933 "toggle_vp_x",
2934 Inkscape::ICON_SIZE_DECORATION );
2935 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2936 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2937 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2938 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2939 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2940 }
2942 /* Angle Y */
2943 {
2944 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2945 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2946 eact = create_adjustment_action( "3DBoxAngleYAction",
2947 _("Angle in Y direction"), _("Angle Y:"),
2948 // Translators: PL is short for 'perspective line'
2949 _("Angle of PLs in Y direction"),
2950 "tools.shapes.3dbox", "box3d_angle_y", 30,
2951 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2952 -360.0, 360.0, 1.0, 10.0,
2953 labels, values, G_N_ELEMENTS(labels),
2954 box3d_angle_y_value_changed );
2955 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2956 g_object_set_data( holder, "box3d_angle_y_action", eact );
2957 box3d_angle_y = eact;
2958 }
2960 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2961 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2962 } else {
2963 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2964 }
2966 /* VP Y state */
2967 {
2968 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2969 // Translators: VP is short for 'vanishing point'
2970 _("State of VP in Y direction"),
2971 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2972 "toggle_vp_y",
2973 Inkscape::ICON_SIZE_DECORATION );
2974 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2975 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2976 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2977 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2978 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2979 }
2981 /* Angle Z */
2982 {
2983 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2984 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2985 eact = create_adjustment_action( "3DBoxAngleZAction",
2986 _("Angle in Z direction"), _("Angle Z:"),
2987 // Translators: PL is short for 'perspective line'
2988 _("Angle of PLs in Z direction"),
2989 "tools.shapes.3dbox", "box3d_angle_z", 30,
2990 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2991 -360.0, 360.0, 1.0, 10.0,
2992 labels, values, G_N_ELEMENTS(labels),
2993 box3d_angle_z_value_changed );
2994 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2995 g_object_set_data( holder, "box3d_angle_z_action", eact );
2996 box3d_angle_z = eact;
2997 }
2999 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3000 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3001 } else {
3002 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3003 }
3005 /* VP Z state */
3006 {
3007 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3008 // Translators: VP is short for 'vanishing point'
3009 _("State of VP in Z direction"),
3010 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3011 "toggle_vp_z",
3012 Inkscape::ICON_SIZE_DECORATION );
3013 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3014 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3015 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3016 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3017 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3018 }
3020 sigc::connection *connection = new sigc::connection(
3021 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3022 );
3023 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3024 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3025 }
3027 //########################
3028 //## Spiral ##
3029 //########################
3031 static void
3032 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3033 {
3034 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3036 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3037 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3038 }
3040 // quit if run by the attr_changed listener
3041 if (g_object_get_data( tbl, "freeze" )) {
3042 return;
3043 }
3045 // in turn, prevent listener from responding
3046 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3048 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3050 bool modmade = false;
3051 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3052 items != NULL;
3053 items = items->next)
3054 {
3055 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3056 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3057 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3058 SP_OBJECT((SPItem *) items->data)->updateRepr();
3059 modmade = true;
3060 }
3061 }
3063 g_free(namespaced_name);
3065 if (modmade) {
3066 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3067 _("Change spiral"));
3068 }
3070 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3071 }
3073 static void
3074 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3075 {
3076 sp_spl_tb_value_changed(adj, tbl, "revolution");
3077 }
3079 static void
3080 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3081 {
3082 sp_spl_tb_value_changed(adj, tbl, "expansion");
3083 }
3085 static void
3086 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3087 {
3088 sp_spl_tb_value_changed(adj, tbl, "t0");
3089 }
3091 static void
3092 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3093 {
3094 GtkWidget *tbl = GTK_WIDGET(obj);
3096 GtkAdjustment *adj;
3098 // fixme: make settable
3099 gdouble rev = 5;
3100 gdouble exp = 1.0;
3101 gdouble t0 = 0.0;
3103 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3104 gtk_adjustment_set_value(adj, rev);
3105 gtk_adjustment_value_changed(adj);
3107 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3108 gtk_adjustment_set_value(adj, exp);
3109 gtk_adjustment_value_changed(adj);
3111 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3112 gtk_adjustment_set_value(adj, t0);
3113 gtk_adjustment_value_changed(adj);
3115 spinbutton_defocus(GTK_OBJECT(tbl));
3116 }
3119 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3120 gchar const */*old_value*/, gchar const */*new_value*/,
3121 bool /*is_interactive*/, gpointer data)
3122 {
3123 GtkWidget *tbl = GTK_WIDGET(data);
3125 // quit if run by the _changed callbacks
3126 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3127 return;
3128 }
3130 // in turn, prevent callbacks from responding
3131 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3133 GtkAdjustment *adj;
3134 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3135 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3137 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3138 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3140 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3141 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3143 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3144 }
3147 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3148 NULL, /* child_added */
3149 NULL, /* child_removed */
3150 spiral_tb_event_attr_changed,
3151 NULL, /* content_changed */
3152 NULL /* order_changed */
3153 };
3155 static void
3156 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3157 {
3158 int n_selected = 0;
3159 Inkscape::XML::Node *repr = NULL;
3161 purge_repr_listener( tbl, tbl );
3163 for (GSList const *items = selection->itemList();
3164 items != NULL;
3165 items = items->next)
3166 {
3167 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3168 n_selected++;
3169 repr = SP_OBJECT_REPR((SPItem *) items->data);
3170 }
3171 }
3173 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3175 if (n_selected == 0) {
3176 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3177 } else if (n_selected == 1) {
3178 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3180 if (repr) {
3181 g_object_set_data( tbl, "repr", repr );
3182 Inkscape::GC::anchor(repr);
3183 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3184 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3185 }
3186 } else {
3187 // FIXME: implement averaging of all parameters for multiple selected
3188 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3189 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3190 }
3191 }
3194 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3195 {
3196 EgeAdjustmentAction* eact = 0;
3197 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3199 {
3200 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3201 ege_output_action_set_use_markup( act, TRUE );
3202 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3203 g_object_set_data( holder, "mode_action", act );
3204 }
3206 /* Revolution */
3207 {
3208 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3209 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3210 eact = create_adjustment_action( "SpiralRevolutionAction",
3211 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3212 "tools.shapes.spiral", "revolution", 3.0,
3213 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3214 0.01, 1024.0, 0.1, 1.0,
3215 labels, values, G_N_ELEMENTS(labels),
3216 sp_spl_tb_revolution_value_changed, 1, 2);
3217 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3218 }
3220 /* Expansion */
3221 {
3222 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3223 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3224 eact = create_adjustment_action( "SpiralExpansionAction",
3225 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3226 "tools.shapes.spiral", "expansion", 1.0,
3227 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3228 0.0, 1000.0, 0.01, 1.0,
3229 labels, values, G_N_ELEMENTS(labels),
3230 sp_spl_tb_expansion_value_changed);
3231 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3232 }
3234 /* T0 */
3235 {
3236 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3237 gdouble values[] = {0, 0.5, 0.9};
3238 eact = create_adjustment_action( "SpiralT0Action",
3239 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3240 "tools.shapes.spiral", "t0", 0.0,
3241 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3242 0.0, 0.999, 0.01, 1.0,
3243 labels, values, G_N_ELEMENTS(labels),
3244 sp_spl_tb_t0_value_changed);
3245 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3246 }
3248 /* Reset */
3249 {
3250 InkAction* inky = ink_action_new( "SpiralResetAction",
3251 _("Defaults"),
3252 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3253 GTK_STOCK_CLEAR,
3254 secondarySize );
3255 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3256 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3257 }
3260 sigc::connection *connection = new sigc::connection(
3261 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3262 );
3263 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3264 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3265 }
3267 //########################
3268 //## Pen/Pencil ##
3269 //########################
3271 static char const *
3272 freehand_tool_name(GObject *dataKludge)
3273 {
3274 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3275 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3276 ? "tools.freehand.pen"
3277 : "tools.freehand.pencil" );
3278 }
3280 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3281 {
3282 prefs_set_int_attribute(freehand_tool_name(tbl), "spiro-spline-mode", ege_select_one_action_get_active(act));
3283 }
3285 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3286 {
3287 /* Freehand mode toggle buttons */
3288 {
3289 // FIXME: spiroMode seems not to be read correctly here during startup, although the
3290 // correct mode is used in pen/pencil tool later on; same for freehand shapes
3291 guint spiroMode = prefs_get_int_attribute(freehand_tool_name(holder), "spiro-spline-mode", 0);
3292 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3294 {
3295 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3297 GtkTreeIter iter;
3298 gtk_list_store_append( model, &iter );
3299 gtk_list_store_set( model, &iter,
3300 0, _("Bezier"),
3301 1, _("Create regular Bezier path"),
3302 2, "bezier_mode",
3303 -1 );
3305 gtk_list_store_append( model, &iter );
3306 gtk_list_store_set( model, &iter,
3307 0, _("Spiro"),
3308 1, _("Create Spiro path"),
3309 2, "spiro_splines_mode",
3310 -1 );
3312 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3313 "FreehandModeActionPencil" :
3314 "FreehandModeActionPen",
3315 ("Mode:"), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3316 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3318 ege_select_one_action_set_appearance( act, "full" );
3319 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3320 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3321 ege_select_one_action_set_icon_column( act, 2 );
3322 ege_select_one_action_set_icon_size( act, secondarySize );
3323 ege_select_one_action_set_tooltip_column( act, 1 );
3325 ege_select_one_action_set_active( act, spiroMode);
3326 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3327 }
3328 }
3329 }
3331 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3332 gint shape = ege_select_one_action_get_active( act );
3333 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3334 }
3336 /**
3337 * \brief Generate the list of freehand advanced shape option entries.
3338 */
3339 GList * freehand_shape_dropdown_items_list() {
3340 GList *glist = NULL;
3342 glist = g_list_append (glist, _("None"));
3343 glist = g_list_append (glist, _("Triangle in"));
3344 glist = g_list_append (glist, _("Triangle out"));
3345 glist = g_list_append (glist, _("Ellipse"));
3346 glist = g_list_append (glist, _("From clipboard"));
3348 return glist;
3349 }
3351 static void
3352 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3353 /*advanced shape options */
3354 {
3355 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3357 GList* items = 0;
3358 gint count = 0;
3359 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3360 {
3361 GtkTreeIter iter;
3362 gtk_list_store_append( model, &iter );
3363 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3364 count++;
3365 }
3366 g_list_free( items );
3367 items = 0;
3368 EgeSelectOneAction* act1 = ege_select_one_action_new(
3369 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3370 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3371 g_object_set( act1, "short_label", _("Shape:"), NULL );
3372 ege_select_one_action_set_appearance( act1, "compact" );
3373 ege_select_one_action_set_active( act1, prefs_get_int_attribute(freehand_tool_name(holder), "shape", 0) );
3374 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3375 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3376 g_object_set_data( holder, "shape_action", act1 );
3377 }
3378 }
3380 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3381 {
3382 sp_add_spiro_toggle(mainActions, holder, false);
3383 sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3384 }
3387 static void
3388 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3389 {
3390 GtkWidget *tbl = GTK_WIDGET(obj);
3392 GtkAdjustment *adj;
3394 // fixme: make settable
3395 gdouble tolerance = 4;
3397 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3398 gtk_adjustment_set_value(adj, tolerance);
3399 gtk_adjustment_value_changed(adj);
3401 spinbutton_defocus(GTK_OBJECT(tbl));
3402 }
3404 static void
3405 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3406 {
3408 // quit if run by the attr_changed listener
3409 if (g_object_get_data( tbl, "freeze" )) {
3410 return;
3411 }
3412 // in turn, prevent listener from responding
3413 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3414 prefs_set_double_attribute("tools.freehand.pencil",
3415 "tolerance", adj->value);
3416 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3418 }
3422 static void
3423 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3424 const gchar */*key*/,
3425 const gchar */*oldval*/,
3426 const gchar */*newval*/,
3427 bool /*is_interactive*/,
3428 void * data)
3429 {
3430 GObject* tbl = G_OBJECT(data);
3431 if (g_object_get_data( tbl, "freeze" )) {
3432 return;
3433 }
3435 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3437 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3438 "tolerance");
3440 double v = prefs_get_double_attribute("tools.freehand.pencil",
3441 "tolerance", adj->value);
3442 gtk_adjustment_set_value(adj, v);
3443 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3445 }
3447 static Inkscape::XML::NodeEventVector pencil_node_events =
3448 {
3449 NULL,
3450 NULL,
3451 sp_pencil_tb_tolerance_value_changed_external,
3452 NULL,
3453 NULL,
3454 };
3457 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3458 {
3459 sp_add_spiro_toggle(mainActions, holder, true);
3461 EgeAdjustmentAction* eact = 0;
3463 /* Tolerance */
3464 {
3465 gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3466 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3467 eact = create_adjustment_action( "PencilToleranceAction",
3468 _("Smoothing:"), _("Smoothing: "),
3469 _("How much smoothing (simplifying) is applied to the line"),
3470 "tools.freehand.pencil", "tolerance",
3471 3.0,
3472 GTK_WIDGET(desktop->canvas), NULL,
3473 holder, TRUE, "altx-pencil",
3474 1, 100.0, 0.5, 0,
3475 labels, values, G_N_ELEMENTS(labels),
3476 sp_pencil_tb_tolerance_value_changed,
3477 1, 2);
3478 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3479 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3481 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3482 "tools.freehand.pencil");
3483 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3484 g_object_set_data(G_OBJECT(holder), "repr", repr);
3486 }
3488 /* advanced shape options */
3489 sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3491 /* Reset */
3492 {
3493 InkAction* inky = ink_action_new( "PencilResetAction",
3494 _("Defaults"),
3495 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3496 GTK_STOCK_CLEAR,
3497 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3498 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3499 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3500 }
3502 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3504 }
3507 //########################
3508 //## Tweak ##
3509 //########################
3511 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3512 {
3513 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3514 }
3516 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3517 {
3518 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3519 }
3521 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3522 {
3523 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3524 }
3526 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3527 {
3528 int mode = ege_select_one_action_get_active( act );
3529 prefs_set_int_attribute("tools.tweak", "mode", mode);
3531 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3532 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3533 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3534 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3535 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3536 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3537 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3538 if (doh) gtk_action_set_sensitive (doh, TRUE);
3539 if (dos) gtk_action_set_sensitive (dos, TRUE);
3540 if (dol) gtk_action_set_sensitive (dol, TRUE);
3541 if (doo) gtk_action_set_sensitive (doo, TRUE);
3542 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3543 if (fid) gtk_action_set_sensitive (fid, FALSE);
3544 } else {
3545 if (doh) gtk_action_set_sensitive (doh, FALSE);
3546 if (dos) gtk_action_set_sensitive (dos, FALSE);
3547 if (dol) gtk_action_set_sensitive (dol, FALSE);
3548 if (doo) gtk_action_set_sensitive (doo, FALSE);
3549 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3550 if (fid) gtk_action_set_sensitive (fid, TRUE);
3551 }
3552 }
3554 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3555 {
3556 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3557 }
3559 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3560 bool show = gtk_toggle_action_get_active( act );
3561 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3562 }
3563 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3564 bool show = gtk_toggle_action_get_active( act );
3565 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3566 }
3567 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3568 bool show = gtk_toggle_action_get_active( act );
3569 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3570 }
3571 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3572 bool show = gtk_toggle_action_get_active( act );
3573 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3574 }
3576 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3577 {
3578 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3580 {
3581 /* Width */
3582 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3583 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3584 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3585 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3586 "tools.tweak", "width", 15,
3587 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3588 1, 100, 1.0, 0.0,
3589 labels, values, G_N_ELEMENTS(labels),
3590 sp_tweak_width_value_changed, 0.01, 0, 100 );
3591 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3592 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3593 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3594 }
3597 {
3598 /* Force */
3599 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3600 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3601 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3602 _("Force"), _("Force:"), _("The force of the tweak action"),
3603 "tools.tweak", "force", 20,
3604 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3605 1, 100, 1.0, 0.0,
3606 labels, values, G_N_ELEMENTS(labels),
3607 sp_tweak_force_value_changed, 0.01, 0, 100 );
3608 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3609 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3610 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3611 }
3613 /* Mode */
3614 {
3615 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3617 GtkTreeIter iter;
3618 gtk_list_store_append( model, &iter );
3619 gtk_list_store_set( model, &iter,
3620 0, _("Push mode"),
3621 1, _("Push parts of paths in any direction"),
3622 2, "tweak_push_mode",
3623 -1 );
3625 gtk_list_store_append( model, &iter );
3626 gtk_list_store_set( model, &iter,
3627 0, _("Shrink mode"),
3628 1, _("Shrink (inset) parts of paths"),
3629 2, "tweak_shrink_mode",
3630 -1 );
3632 gtk_list_store_append( model, &iter );
3633 gtk_list_store_set( model, &iter,
3634 0, _("Grow mode"),
3635 1, _("Grow (outset) parts of paths"),
3636 2, "tweak_grow_mode",
3637 -1 );
3639 gtk_list_store_append( model, &iter );
3640 gtk_list_store_set( model, &iter,
3641 0, _("Attract mode"),
3642 1, _("Attract parts of paths towards cursor"),
3643 2, "tweak_attract_mode",
3644 -1 );
3646 gtk_list_store_append( model, &iter );
3647 gtk_list_store_set( model, &iter,
3648 0, _("Repel mode"),
3649 1, _("Repel parts of paths from cursor"),
3650 2, "tweak_repel_mode",
3651 -1 );
3653 gtk_list_store_append( model, &iter );
3654 gtk_list_store_set( model, &iter,
3655 0, _("Roughen mode"),
3656 1, _("Roughen parts of paths"),
3657 2, "tweak_roughen_mode",
3658 -1 );
3660 gtk_list_store_append( model, &iter );
3661 gtk_list_store_set( model, &iter,
3662 0, _("Color paint mode"),
3663 1, _("Paint the tool's color upon selected objects"),
3664 2, "tweak_colorpaint_mode",
3665 -1 );
3667 gtk_list_store_append( model, &iter );
3668 gtk_list_store_set( model, &iter,
3669 0, _("Color jitter mode"),
3670 1, _("Jitter the colors of selected objects"),
3671 2, "tweak_colorjitter_mode",
3672 -1 );
3674 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3675 g_object_set( act, "short_label", _("Mode:"), NULL );
3676 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3677 g_object_set_data( holder, "mode_action", act );
3679 ege_select_one_action_set_appearance( act, "full" );
3680 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3681 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3682 ege_select_one_action_set_icon_column( act, 2 );
3683 ege_select_one_action_set_icon_size( act, secondarySize );
3684 ege_select_one_action_set_tooltip_column( act, 1 );
3686 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3687 ege_select_one_action_set_active( act, mode );
3688 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3690 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3691 }
3693 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3695 {
3696 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3697 ege_output_action_set_use_markup( act, TRUE );
3698 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3699 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3700 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3701 g_object_set_data( holder, "tweak_channels_label", act);
3702 }
3704 {
3705 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3706 _("Hue"),
3707 _("In color mode, act on objects' hue"),
3708 NULL,
3709 Inkscape::ICON_SIZE_DECORATION );
3710 //TRANSLATORS: "H" here stands for hue
3711 g_object_set( act, "short_label", _("H"), NULL );
3712 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3713 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3714 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3715 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3716 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3717 g_object_set_data( holder, "tweak_doh", act);
3718 }
3719 {
3720 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3721 _("Saturation"),
3722 _("In color mode, act on objects' saturation"),
3723 NULL,
3724 Inkscape::ICON_SIZE_DECORATION );
3725 //TRANSLATORS: "S" here stands for Saturation
3726 g_object_set( act, "short_label", _("S"), NULL );
3727 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3728 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3729 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3730 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3731 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3732 g_object_set_data( holder, "tweak_dos", act );
3733 }
3734 {
3735 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3736 _("Lightness"),
3737 _("In color mode, act on objects' lightness"),
3738 NULL,
3739 Inkscape::ICON_SIZE_DECORATION );
3740 //TRANSLATORS: "L" here stands for Lightness
3741 g_object_set( act, "short_label", _("L"), NULL );
3742 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3743 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3744 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3745 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3746 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3747 g_object_set_data( holder, "tweak_dol", act );
3748 }
3749 {
3750 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3751 _("Opacity"),
3752 _("In color mode, act on objects' opacity"),
3753 NULL,
3754 Inkscape::ICON_SIZE_DECORATION );
3755 //TRANSLATORS: "O" here stands for Opacity
3756 g_object_set( act, "short_label", _("O"), NULL );
3757 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3758 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3759 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3760 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3761 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3762 g_object_set_data( holder, "tweak_doo", act );
3763 }
3765 { /* Fidelity */
3766 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3767 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3768 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3769 _("Fidelity"), _("Fidelity:"),
3770 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3771 "tools.tweak", "fidelity", 50,
3772 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3773 1, 100, 1.0, 10.0,
3774 labels, values, G_N_ELEMENTS(labels),
3775 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3776 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3777 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3778 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3779 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3780 g_object_set_data( holder, "tweak_fidelity", eact );
3781 }
3784 /* Use Pressure button */
3785 {
3786 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3787 _("Pressure"),
3788 _("Use the pressure of the input device to alter the force of tweak action"),
3789 "use_pressure",
3790 Inkscape::ICON_SIZE_DECORATION );
3791 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3792 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3793 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3794 }
3796 }
3799 //########################
3800 //## Calligraphy ##
3801 //########################
3802 static void update_presets_list (GObject *tbl)
3803 {
3804 if (g_object_get_data(tbl, "presets_blocked"))
3805 return;
3807 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3808 if (!sel) {
3809 ege_select_one_action_set_active(sel, 0);
3810 return;
3811 }
3813 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3815 for (int i = 1; i <= total_prefs; i++) {
3816 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3817 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3819 bool match = true;
3821 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3822 iter;
3823 ++iter ) {
3824 const gchar *attr_name = g_quark_to_string(iter->key);
3825 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3826 continue;
3827 void *widget = g_object_get_data(tbl, attr_name);
3828 if (widget) {
3829 if (GTK_IS_ADJUSTMENT(widget)) {
3830 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3831 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3832 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3833 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3834 match = false;
3835 break;
3836 }
3837 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3838 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3839 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3840 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3841 if (gtk_toggle_action_get_active(toggle) != v) {
3842 match = false;
3843 break;
3844 }
3845 }
3846 }
3847 }
3849 if (match) {
3850 // newly added item is at the same index as the
3851 // save command, so we need to change twice for it to take effect
3852 ege_select_one_action_set_active(sel, 0);
3853 ege_select_one_action_set_active(sel, i);
3854 return;
3855 }
3856 }
3858 // no match found
3859 ege_select_one_action_set_active(sel, 0);
3860 }
3862 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3863 {
3864 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3865 update_presets_list(tbl);
3866 }
3868 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3869 {
3870 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3871 update_presets_list(tbl);
3872 }
3874 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3875 {
3876 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3877 update_presets_list(tbl);
3878 }
3880 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3881 {
3882 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3883 update_presets_list(tbl);
3884 }
3886 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3887 {
3888 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3889 update_presets_list(tbl);
3890 }
3892 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3893 {
3894 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3895 update_presets_list(tbl);
3896 }
3898 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3899 {
3900 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3901 update_presets_list(tbl);
3902 }
3904 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3905 {
3906 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3907 update_presets_list(tbl);
3908 }
3910 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3911 {
3912 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3913 update_presets_list(tbl);
3914 }
3916 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3917 {
3918 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3919 update_presets_list(tbl);
3920 }
3922 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3923 {
3924 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3925 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3926 update_presets_list(tbl);
3927 if (calligraphy_angle )
3928 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3929 }
3932 #define NUMBER_OF_PRESET_PARAMS 11
3933 static gchar * widget_names[NUMBER_OF_PRESET_PARAMS] = {
3934 "width",
3935 "mass",
3936 "wiggle",
3937 "angle",
3938 "thinning",
3939 "tremor",
3940 "flatness",
3941 "cap_rounding",
3942 "usepressure",
3943 "tracebackground",
3944 "usetilt"
3945 };
3948 static void sp_dcc_build_presets_list(GObject *tbl)
3949 {
3950 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3952 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3953 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3954 gtk_list_store_clear (model);
3956 {
3957 GtkTreeIter iter;
3958 gtk_list_store_append( model, &iter );
3959 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3960 }
3962 //TODO: switch back to prefs API
3963 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3964 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3965 int ii=1;
3966 while (child_repr) {
3967 GtkTreeIter iter;
3968 char *preset_name = (char *) child_repr->attribute("name");
3969 gtk_list_store_append( model, &iter );
3970 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3971 child_repr = sp_repr_next(child_repr);
3972 }
3974 {
3975 GtkTreeIter iter;
3976 gtk_list_store_append( model, &iter );
3977 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
3978 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
3979 }
3981 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
3983 update_presets_list (tbl);
3984 }
3986 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
3987 {
3988 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
3989 if (! desktop) return;
3991 if (g_object_get_data(tbl, "presets_blocked"))
3992 return;
3994 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3995 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
3996 // dialog cancelled
3997 update_presets_list (tbl);
3998 return;
3999 }
4000 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4002 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4003 // empty name entered
4004 update_presets_list (tbl);
4005 return;
4006 }
4008 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4010 int new_index = -1;
4011 gchar *pref_path = NULL;
4012 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4014 for (int i = 1; i <= total_prefs; i++) {
4015 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4016 const gchar *name = prefs_get_string_attribute(path, "name");
4017 if (name && !strcmp(name, profile_name.c_str())) {
4018 // we already have preset with this name, replace its values
4019 new_index = i;
4020 pref_path = g_strdup(path);
4021 break;
4022 }
4023 }
4025 if (new_index == -1) {
4026 // no preset with this name, create
4027 new_index = total_prefs + 1;
4028 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4029 pref_path = create_pref("tools.calligraphic.preset", profile_id);
4030 free(profile_id);
4031 }
4033 for (unsigned i = 0; i < NUMBER_OF_PRESET_PARAMS; ++i) {
4034 gchar *widget_name = widget_names[i];
4035 void *widget = g_object_get_data(tbl, widget_name);
4036 if (widget) {
4037 if (GTK_IS_ADJUSTMENT(widget)) {
4038 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4039 double v = gtk_adjustment_get_value(adj);
4040 prefs_set_double_attribute(pref_path, widget_name, v);
4041 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4042 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4043 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4044 int v = gtk_toggle_action_get_active(toggle);
4045 prefs_set_int_attribute(pref_path, widget_name, v);
4046 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4047 } else {
4048 g_warning("Unknown widget type for preset: %s\n", widget_name);
4049 }
4050 } else {
4051 g_warning("Bad key when writing preset: %s\n", widget_name);
4052 }
4053 }
4054 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4056 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4058 sp_dcc_build_presets_list (tbl);
4060 free (pref_path);
4061 }
4064 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4066 gint preset_index = ege_select_one_action_get_active( act );
4067 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4069 if (preset_index == save_presets_index) {
4070 // this is the Save command
4071 sp_dcc_save_profile(NULL, tbl);
4072 return;
4073 }
4075 if (g_object_get_data(tbl, "presets_blocked"))
4076 return;
4078 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4080 if (preset_path) {
4081 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
4083 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4085 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4086 iter;
4087 ++iter ) {
4088 const gchar *attr_name = g_quark_to_string(iter->key);
4089 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4090 continue;
4091 void *widget = g_object_get_data(tbl, attr_name);
4092 if (widget) {
4093 if (GTK_IS_ADJUSTMENT(widget)) {
4094 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4095 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4096 gtk_adjustment_set_value(adj, v);
4097 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4098 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4099 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4100 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4101 gtk_toggle_action_set_active(toggle, v);
4102 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4103 } else {
4104 g_warning("Unknown widget type for preset: %s\n", attr_name);
4105 }
4106 } else {
4107 g_warning("Bad key found in a preset record: %s\n", attr_name);
4108 }
4109 }
4110 free(preset_path);
4111 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4112 }
4114 }
4117 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4118 {
4119 {
4120 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4122 EgeAdjustmentAction* calligraphy_angle = 0;
4124 {
4125 /* Width */
4126 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4127 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4128 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4129 _("Pen Width"), _("Width:"),
4130 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4131 "tools.calligraphic", "width", 15,
4132 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4133 1, 100, 1.0, 0.0,
4134 labels, values, G_N_ELEMENTS(labels),
4135 sp_ddc_width_value_changed, 0.01, 0, 100 );
4136 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4137 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4138 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4139 }
4141 {
4142 /* Thinning */
4143 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4144 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4145 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4146 _("Stroke Thinning"), _("Thinning:"),
4147 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4148 "tools.calligraphic", "thinning", 10,
4149 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4150 -100, 100, 1, 0.1,
4151 labels, values, G_N_ELEMENTS(labels),
4152 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4153 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4154 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4155 }
4157 {
4158 /* Angle */
4159 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4160 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4161 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4162 _("Pen Angle"), _("Angle:"),
4163 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4164 "tools.calligraphic", "angle", 30,
4165 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4166 -90.0, 90.0, 1.0, 10.0,
4167 labels, values, G_N_ELEMENTS(labels),
4168 sp_ddc_angle_value_changed, 1, 0 );
4169 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4170 g_object_set_data( holder, "angle_action", eact );
4171 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4172 calligraphy_angle = eact;
4173 }
4175 {
4176 /* Fixation */
4177 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4178 gdouble values[] = {0, 20, 40, 60, 90, 100};
4179 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4180 _("Fixation"), _("Fixation:"),
4181 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4182 "tools.calligraphic", "flatness", 90,
4183 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4184 0.0, 100, 1.0, 10.0,
4185 labels, values, G_N_ELEMENTS(labels),
4186 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4187 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4188 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4189 }
4191 {
4192 /* Cap Rounding */
4193 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4194 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4195 // TRANSLATORS: "cap" means "end" (both start and finish) here
4196 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4197 _("Cap rounding"), _("Caps:"),
4198 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4199 "tools.calligraphic", "cap_rounding", 0.0,
4200 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4201 0.0, 5.0, 0.01, 0.1,
4202 labels, values, G_N_ELEMENTS(labels),
4203 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4204 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4205 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4206 }
4208 {
4209 /* Tremor */
4210 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4211 gdouble values[] = {0, 10, 20, 40, 60, 100};
4212 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4213 _("Stroke Tremor"), _("Tremor:"),
4214 _("Increase to make strokes rugged and trembling"),
4215 "tools.calligraphic", "tremor", 0.0,
4216 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4217 0.0, 100, 1, 0.0,
4218 labels, values, G_N_ELEMENTS(labels),
4219 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4221 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4222 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4223 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4224 }
4226 {
4227 /* Wiggle */
4228 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4229 gdouble values[] = {0, 20, 40, 60, 100};
4230 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4231 _("Pen Wiggle"), _("Wiggle:"),
4232 _("Increase to make the pen waver and wiggle"),
4233 "tools.calligraphic", "wiggle", 0.0,
4234 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4235 0.0, 100, 1, 0.0,
4236 labels, values, G_N_ELEMENTS(labels),
4237 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4238 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4239 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4240 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4241 }
4243 {
4244 /* Mass */
4245 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4246 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4247 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4248 _("Pen Mass"), _("Mass:"),
4249 _("Increase to make the pen drag behind, as if slowed by inertia"),
4250 "tools.calligraphic", "mass", 2.0,
4251 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4252 0.0, 100, 1, 0.0,
4253 labels, values, G_N_ELEMENTS(labels),
4254 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4255 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4256 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4257 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4258 }
4261 /* Trace Background button */
4262 {
4263 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4264 _("Trace Background"),
4265 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4266 "trace_background",
4267 Inkscape::ICON_SIZE_DECORATION );
4268 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4269 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4270 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4271 g_object_set_data( holder, "tracebackground", act );
4272 }
4274 /* Use Pressure button */
4275 {
4276 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4277 _("Pressure"),
4278 _("Use the pressure of the input device to alter the width of the pen"),
4279 "use_pressure",
4280 Inkscape::ICON_SIZE_DECORATION );
4281 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4282 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4283 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4284 g_object_set_data( holder, "usepressure", act );
4285 }
4287 /* Use Tilt button */
4288 {
4289 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4290 _("Tilt"),
4291 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4292 "use_tilt",
4293 Inkscape::ICON_SIZE_DECORATION );
4294 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4295 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4296 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4297 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4298 g_object_set_data( holder, "usetilt", act );
4299 }
4301 /*calligraphic profile */
4302 {
4303 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4304 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model));
4305 ege_select_one_action_set_appearance (act1, "compact");
4306 g_object_set_data (holder, "profile_selector", act1 );
4308 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4310 sp_dcc_build_presets_list (holder);
4312 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4313 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4314 }
4315 }
4316 }
4319 //########################
4320 //## Circle / Arc ##
4321 //########################
4323 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4324 {
4325 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4326 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4328 if (v1 == 0 && v2 == 0) {
4329 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4330 gtk_action_set_sensitive( ocb, FALSE );
4331 gtk_action_set_sensitive( make_whole, FALSE );
4332 }
4333 } else {
4334 gtk_action_set_sensitive( ocb, TRUE );
4335 gtk_action_set_sensitive( make_whole, TRUE );
4336 }
4337 }
4339 static void
4340 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4341 {
4342 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4344 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4345 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4346 }
4348 // quit if run by the attr_changed listener
4349 if (g_object_get_data( tbl, "freeze" )) {
4350 return;
4351 }
4353 // in turn, prevent listener from responding
4354 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4356 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4358 bool modmade = false;
4359 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4360 items != NULL;
4361 items = items->next)
4362 {
4363 SPItem *item = SP_ITEM(items->data);
4365 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4367 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4368 SPArc *arc = SP_ARC(item);
4370 if (!strcmp(value_name, "start"))
4371 ge->start = (adj->value * M_PI)/ 180;
4372 else
4373 ge->end = (adj->value * M_PI)/ 180;
4375 sp_genericellipse_normalize(ge);
4376 ((SPObject *)arc)->updateRepr();
4377 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4379 modmade = true;
4380 }
4381 }
4383 g_free(namespaced_name);
4385 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4387 sp_arctb_sensitivize( tbl, adj->value, other->value );
4389 if (modmade) {
4390 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4391 _("Arc: Change start/end"));
4392 }
4394 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4395 }
4398 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4399 {
4400 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4401 }
4403 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4404 {
4405 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4406 }
4409 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4410 {
4411 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4412 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4413 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4414 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4415 }
4417 // only take action if run by the attr_changed listener
4418 if (!g_object_get_data( tbl, "freeze" )) {
4419 // in turn, prevent listener from responding
4420 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4422 if ( eraserMode != 0 ) {
4423 } else {
4424 }
4425 // TODO finish implementation
4427 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4428 }
4429 }
4431 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4432 {
4433 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4434 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4435 if ( ege_select_one_action_get_active( act ) != 0 ) {
4436 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4437 } else {
4438 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4439 }
4440 }
4442 // quit if run by the attr_changed listener
4443 if (g_object_get_data( tbl, "freeze" )) {
4444 return;
4445 }
4447 // in turn, prevent listener from responding
4448 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4450 bool modmade = false;
4452 if ( ege_select_one_action_get_active(act) != 0 ) {
4453 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4454 items != NULL;
4455 items = items->next)
4456 {
4457 if (SP_IS_ARC((SPItem *) items->data)) {
4458 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4459 repr->setAttribute("sodipodi:open", "true");
4460 SP_OBJECT((SPItem *) items->data)->updateRepr();
4461 modmade = true;
4462 }
4463 }
4464 } else {
4465 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4466 items != NULL;
4467 items = items->next)
4468 {
4469 if (SP_IS_ARC((SPItem *) items->data)) {
4470 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4471 repr->setAttribute("sodipodi:open", NULL);
4472 SP_OBJECT((SPItem *) items->data)->updateRepr();
4473 modmade = true;
4474 }
4475 }
4476 }
4478 if (modmade) {
4479 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4480 _("Arc: Change open/closed"));
4481 }
4483 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4484 }
4486 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4487 {
4488 GtkAdjustment *adj;
4489 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4490 gtk_adjustment_set_value(adj, 0.0);
4491 gtk_adjustment_value_changed(adj);
4493 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4494 gtk_adjustment_set_value(adj, 0.0);
4495 gtk_adjustment_value_changed(adj);
4497 spinbutton_defocus( GTK_OBJECT(obj) );
4498 }
4500 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4501 gchar const */*old_value*/, gchar const */*new_value*/,
4502 bool /*is_interactive*/, gpointer data)
4503 {
4504 GObject *tbl = G_OBJECT(data);
4506 // quit if run by the _changed callbacks
4507 if (g_object_get_data( tbl, "freeze" )) {
4508 return;
4509 }
4511 // in turn, prevent callbacks from responding
4512 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4514 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4515 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4517 GtkAdjustment *adj1,*adj2;
4518 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4519 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4520 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4521 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4523 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4525 char const *openstr = NULL;
4526 openstr = repr->attribute("sodipodi:open");
4527 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4529 if (openstr) {
4530 ege_select_one_action_set_active( ocb, 1 );
4531 } else {
4532 ege_select_one_action_set_active( ocb, 0 );
4533 }
4535 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4536 }
4538 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4539 NULL, /* child_added */
4540 NULL, /* child_removed */
4541 arc_tb_event_attr_changed,
4542 NULL, /* content_changed */
4543 NULL /* order_changed */
4544 };
4547 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4548 {
4549 int n_selected = 0;
4550 Inkscape::XML::Node *repr = NULL;
4552 purge_repr_listener( tbl, tbl );
4554 for (GSList const *items = selection->itemList();
4555 items != NULL;
4556 items = items->next)
4557 {
4558 if (SP_IS_ARC((SPItem *) items->data)) {
4559 n_selected++;
4560 repr = SP_OBJECT_REPR((SPItem *) items->data);
4561 }
4562 }
4564 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4566 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4567 if (n_selected == 0) {
4568 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4569 } else if (n_selected == 1) {
4570 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4571 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4573 if (repr) {
4574 g_object_set_data( tbl, "repr", repr );
4575 Inkscape::GC::anchor(repr);
4576 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4577 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4578 }
4579 } else {
4580 // FIXME: implement averaging of all parameters for multiple selected
4581 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4582 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4583 sp_arctb_sensitivize( tbl, 1, 0 );
4584 }
4585 }
4588 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4589 {
4590 EgeAdjustmentAction* eact = 0;
4591 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4594 {
4595 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4596 ege_output_action_set_use_markup( act, TRUE );
4597 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4598 g_object_set_data( holder, "mode_action", act );
4599 }
4601 /* Start */
4602 {
4603 eact = create_adjustment_action( "ArcStartAction",
4604 _("Start"), _("Start:"),
4605 _("The angle (in degrees) from the horizontal to the arc's start point"),
4606 "tools.shapes.arc", "start", 0.0,
4607 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4608 -360.0, 360.0, 1.0, 10.0,
4609 0, 0, 0,
4610 sp_arctb_start_value_changed);
4611 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4612 }
4614 /* End */
4615 {
4616 eact = create_adjustment_action( "ArcEndAction",
4617 _("End"), _("End:"),
4618 _("The angle (in degrees) from the horizontal to the arc's end point"),
4619 "tools.shapes.arc", "end", 0.0,
4620 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4621 -360.0, 360.0, 1.0, 10.0,
4622 0, 0, 0,
4623 sp_arctb_end_value_changed);
4624 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4625 }
4627 /* Segments / Pie checkbox */
4628 {
4629 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4631 GtkTreeIter iter;
4632 gtk_list_store_append( model, &iter );
4633 gtk_list_store_set( model, &iter,
4634 0, _("Closed arc"),
4635 1, _("Switch to segment (closed shape with two radii)"),
4636 2, "circle_closed_arc",
4637 -1 );
4639 gtk_list_store_append( model, &iter );
4640 gtk_list_store_set( model, &iter,
4641 0, _("Open Arc"),
4642 1, _("Switch to arc (unclosed shape)"),
4643 2, "circle_open_arc",
4644 -1 );
4646 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4647 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4648 g_object_set_data( holder, "open_action", act );
4650 ege_select_one_action_set_appearance( act, "full" );
4651 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4652 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4653 ege_select_one_action_set_icon_column( act, 2 );
4654 ege_select_one_action_set_icon_size( act, secondarySize );
4655 ege_select_one_action_set_tooltip_column( act, 1 );
4657 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4658 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4659 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4660 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4661 }
4663 /* Make Whole */
4664 {
4665 InkAction* inky = ink_action_new( "ArcResetAction",
4666 _("Make whole"),
4667 _("Make the shape a whole ellipse, not arc or segment"),
4668 "reset_circle",
4669 secondarySize );
4670 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4671 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4672 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4673 g_object_set_data( holder, "make_whole", inky );
4674 }
4676 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4677 // sensitivize make whole and open checkbox
4678 {
4679 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4680 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4681 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4682 }
4685 sigc::connection *connection = new sigc::connection(
4686 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4687 );
4688 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4689 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4690 }
4695 // toggle button callbacks and updaters
4697 //########################
4698 //## Dropper ##
4699 //########################
4701 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4702 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4703 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4704 if ( set_action ) {
4705 if ( gtk_toggle_action_get_active( act ) ) {
4706 gtk_action_set_sensitive( set_action, TRUE );
4707 } else {
4708 gtk_action_set_sensitive( set_action, FALSE );
4709 }
4710 }
4712 spinbutton_defocus(GTK_OBJECT(tbl));
4713 }
4715 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4716 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4717 spinbutton_defocus(GTK_OBJECT(tbl));
4718 }
4721 /**
4722 * Dropper auxiliary toolbar construction and setup.
4723 *
4724 * TODO: Would like to add swatch of current color.
4725 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4726 * can drag and drop places. Will provide a nice mixing palette.
4727 */
4728 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4729 {
4730 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4732 {
4733 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4734 ege_output_action_set_use_markup( act, TRUE );
4735 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4736 }
4738 {
4739 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4740 _("Pick opacity"),
4741 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4742 NULL,
4743 Inkscape::ICON_SIZE_DECORATION );
4744 g_object_set( act, "short_label", _("Pick"), NULL );
4745 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4746 g_object_set_data( holder, "pick_action", act );
4747 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4748 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4749 }
4751 {
4752 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4753 _("Assign opacity"),
4754 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4755 NULL,
4756 Inkscape::ICON_SIZE_DECORATION );
4757 g_object_set( act, "short_label", _("Assign"), NULL );
4758 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4759 g_object_set_data( holder, "set_action", act );
4760 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4761 // make sure it's disabled if we're not picking alpha
4762 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4763 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4764 }
4765 }
4769 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4770 {
4771 {
4772 /* Width */
4773 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4774 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4775 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4776 _("Pen Width"), _("Width:"),
4777 _("The width of the eraser pen (relative to the visible canvas area)"),
4778 "tools.eraser", "width", 15,
4779 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4780 1, 100, 1.0, 0.0,
4781 labels, values, G_N_ELEMENTS(labels),
4782 sp_ddc_width_value_changed, 0.01, 0, 100 );
4783 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4784 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4785 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4786 }
4788 {
4789 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4791 GtkTreeIter iter;
4792 gtk_list_store_append( model, &iter );
4793 gtk_list_store_set( model, &iter,
4794 0, _("Delete"),
4795 1, _("Delete objects touched by the eraser"),
4796 2, "delete_object",
4797 -1 );
4799 gtk_list_store_append( model, &iter );
4800 gtk_list_store_set( model, &iter,
4801 0, _("Cut"),
4802 1, _("Cut out from objects"),
4803 2, "difference",
4804 -1 );
4806 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4807 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4808 g_object_set_data( holder, "eraser_mode_action", act );
4810 ege_select_one_action_set_appearance( act, "full" );
4811 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4812 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4813 ege_select_one_action_set_icon_column( act, 2 );
4814 ege_select_one_action_set_tooltip_column( act, 1 );
4816 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4817 ege_select_one_action_set_active( act, eraserMode );
4818 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4819 }
4821 }
4823 //########################
4824 //## Text Toolbox ##
4825 //########################
4826 /*
4827 static void
4828 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4829 {
4830 //Call back for letter sizing spinbutton
4831 }
4833 static void
4834 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4835 {
4836 //Call back for line height spinbutton
4837 }
4839 static void
4840 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4841 {
4842 //Call back for horizontal kerning spinbutton
4843 }
4845 static void
4846 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4847 {
4848 //Call back for vertical kerning spinbutton
4849 }
4851 static void
4852 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4853 {
4854 //Call back for letter rotation spinbutton
4855 }*/
4857 namespace {
4859 bool popdown_visible = false;
4860 bool popdown_hasfocus = false;
4862 void
4863 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4864 {
4865 SPStyle *query =
4866 sp_style_new (SP_ACTIVE_DOCUMENT);
4868 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4870 int result_family =
4871 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4873 int result_style =
4874 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4876 int result_numbers =
4877 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4879 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4881 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4882 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
4883 // there are no texts in selection, read from prefs
4885 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4886 if (repr) {
4887 sp_style_read_from_repr (query, repr);
4888 if (g_object_get_data(tbl, "text_style_from_prefs")) {
4889 // do not reset the toolbar style from prefs if we already did it last time
4890 sp_style_unref(query);
4891 return;
4892 }
4893 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
4894 } else {
4895 sp_style_unref(query);
4896 return;
4897 }
4898 } else {
4899 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
4900 }
4902 if (query->text)
4903 {
4904 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4905 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4906 gtk_entry_set_text (GTK_ENTRY (entry), "");
4908 } else if (query->text->font_specification.value || query->text->font_family.value) {
4910 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4912 // Get the font that corresponds
4913 Glib::ustring familyName;
4915 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4916 if (font) {
4917 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4918 font->Unref();
4919 font = NULL;
4920 }
4922 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4924 Gtk::TreePath path;
4925 try {
4926 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4927 } catch (...) {
4928 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4929 sp_style_unref(query);
4930 return;
4931 }
4933 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4934 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4936 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4938 gtk_tree_selection_select_path (tselection, path.gobj());
4939 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4941 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4942 }
4944 //Size
4945 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4946 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4947 g_object_set_data (tbl, "size-block", gpointer(1));
4948 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4949 g_object_set_data (tbl, "size-block", gpointer(0));
4950 free (str);
4952 //Anchor
4953 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4954 {
4955 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4956 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4957 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4958 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4959 }
4960 else
4961 {
4962 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4963 {
4964 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4965 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4966 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4967 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4968 }
4969 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4970 {
4971 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4972 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4973 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4974 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4975 }
4976 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4977 {
4978 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4979 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4980 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4981 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4982 }
4983 }
4985 //Style
4986 {
4987 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4989 gboolean active = gtk_toggle_button_get_active (button);
4990 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4992 if (active != check)
4993 {
4994 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4995 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4996 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4997 }
4998 }
5000 {
5001 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5003 gboolean active = gtk_toggle_button_get_active (button);
5004 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5006 if (active != check)
5007 {
5008 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5009 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5010 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5011 }
5012 }
5014 //Orientation
5015 //locking both buttons, changing one affect all group (both)
5016 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5017 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5019 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5020 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5022 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5023 {
5024 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5025 }
5026 else
5027 {
5028 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5029 }
5030 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5031 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5032 }
5034 sp_style_unref(query);
5035 }
5037 void
5038 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5039 {
5040 sp_text_toolbox_selection_changed (selection, tbl);
5041 }
5043 void
5044 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5045 {
5046 sp_text_toolbox_selection_changed (NULL, tbl);
5047 }
5049 void
5050 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5051 GObject *tbl)
5052 {
5053 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5054 GtkTreeModel *model = 0;
5055 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5056 GtkTreeIter iter;
5057 char *family = 0;
5059 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5060 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5062 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5063 return;
5064 }
5066 gtk_tree_model_get (model, &iter, 0, &family, -1);
5068 if (g_object_get_data (G_OBJECT (selection), "block"))
5069 {
5070 gtk_entry_set_text (GTK_ENTRY (entry), family);
5071 return;
5072 }
5074 gtk_entry_set_text (GTK_ENTRY (entry), family);
5076 SPStyle *query =
5077 sp_style_new (SP_ACTIVE_DOCUMENT);
5079 int result_fontspec =
5080 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5082 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5084 SPCSSAttr *css = sp_repr_css_attr_new ();
5087 // First try to get the font spec from the stored value
5088 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5090 if (fontSpec.empty()) {
5091 // Construct a new font specification if it does not yet exist
5092 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5093 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5094 fontFromStyle->Unref();
5095 }
5097 if (!fontSpec.empty()) {
5098 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5099 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5100 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5101 if (font) {
5102 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5104 // Set all the these just in case they were altered when finding the best
5105 // match for the new family and old style...
5107 gchar c[256];
5109 font->Family(c, 256);
5110 sp_repr_css_set_property (css, "font-family", c);
5112 font->Attribute( "weight", c, 256);
5113 sp_repr_css_set_property (css, "font-weight", c);
5115 font->Attribute("style", c, 256);
5116 sp_repr_css_set_property (css, "font-style", c);
5118 font->Attribute("stretch", c, 256);
5119 sp_repr_css_set_property (css, "font-stretch", c);
5121 font->Attribute("variant", c, 256);
5122 sp_repr_css_set_property (css, "font-variant", c);
5124 font->Unref();
5125 }
5126 }
5127 }
5129 // If querying returned nothing, set the default style of the tool (for new texts)
5130 if (result_fontspec == QUERY_STYLE_NOTHING)
5131 {
5132 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5133 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5134 }
5135 else
5136 {
5137 sp_desktop_set_style (desktop, css, true, true);
5138 }
5140 sp_style_unref(query);
5142 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5143 _("Text: Change font family"));
5144 sp_repr_css_attr_unref (css);
5145 free (family);
5146 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5148 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5149 }
5151 void
5152 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5153 GObject *tbl)
5154 {
5155 const char *family = gtk_entry_get_text (entry);
5157 try {
5158 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5159 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5160 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5161 gtk_tree_selection_select_path (selection, path.gobj());
5162 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5163 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5164 } catch (...) {
5165 if (family && strlen (family))
5166 {
5167 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5168 }
5169 }
5170 }
5172 void
5173 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5174 gpointer data)
5175 {
5176 if (g_object_get_data (G_OBJECT (button), "block")) return;
5177 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5178 int prop = GPOINTER_TO_INT(data);
5180 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5181 SPCSSAttr *css = sp_repr_css_attr_new ();
5183 switch (prop)
5184 {
5185 case 0:
5186 {
5187 sp_repr_css_set_property (css, "text-anchor", "start");
5188 sp_repr_css_set_property (css, "text-align", "start");
5189 break;
5190 }
5191 case 1:
5192 {
5193 sp_repr_css_set_property (css, "text-anchor", "middle");
5194 sp_repr_css_set_property (css, "text-align", "center");
5195 break;
5196 }
5198 case 2:
5199 {
5200 sp_repr_css_set_property (css, "text-anchor", "end");
5201 sp_repr_css_set_property (css, "text-align", "end");
5202 break;
5203 }
5205 case 3:
5206 {
5207 sp_repr_css_set_property (css, "text-anchor", "start");
5208 sp_repr_css_set_property (css, "text-align", "justify");
5209 break;
5210 }
5211 }
5213 SPStyle *query =
5214 sp_style_new (SP_ACTIVE_DOCUMENT);
5215 int result_numbers =
5216 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5218 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5219 if (result_numbers == QUERY_STYLE_NOTHING)
5220 {
5221 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5222 }
5224 sp_style_unref(query);
5226 sp_desktop_set_style (desktop, css, true, true);
5227 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5228 _("Text: Change alignment"));
5229 sp_repr_css_attr_unref (css);
5231 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5232 }
5234 void
5235 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5236 gpointer data)
5237 {
5238 if (g_object_get_data (G_OBJECT (button), "block")) return;
5240 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5241 SPCSSAttr *css = sp_repr_css_attr_new ();
5242 int prop = GPOINTER_TO_INT(data);
5243 bool active = gtk_toggle_button_get_active (button);
5245 SPStyle *query =
5246 sp_style_new (SP_ACTIVE_DOCUMENT);
5248 int result_fontspec =
5249 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5251 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5252 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5253 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5255 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5256 Glib::ustring newFontSpec = "";
5258 if (fontSpec.empty()) {
5259 // Construct a new font specification if it does not yet exist
5260 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5261 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5262 fontFromStyle->Unref();
5263 }
5265 switch (prop)
5266 {
5267 case 0:
5268 {
5269 if (!fontSpec.empty()) {
5270 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5271 }
5272 if (fontSpec != newFontSpec) {
5273 // Don't even set the bold if the font didn't exist on the system
5274 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5275 }
5276 break;
5277 }
5279 case 1:
5280 {
5281 if (!fontSpec.empty()) {
5282 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5283 }
5284 if (fontSpec != newFontSpec) {
5285 // Don't even set the italic if the font didn't exist on the system
5286 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5287 }
5288 break;
5289 }
5290 }
5292 if (!newFontSpec.empty()) {
5293 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5294 }
5296 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5297 if (result_fontspec == QUERY_STYLE_NOTHING)
5298 {
5299 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5300 }
5302 sp_style_unref(query);
5304 sp_desktop_set_style (desktop, css, true, true);
5305 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5306 _("Text: Change font style"));
5307 sp_repr_css_attr_unref (css);
5309 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5310 }
5312 void
5313 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5314 gpointer data)
5315 {
5316 if (g_object_get_data (G_OBJECT (button), "block")) {
5317 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5318 return;
5319 }
5321 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5322 SPCSSAttr *css = sp_repr_css_attr_new ();
5323 int prop = GPOINTER_TO_INT(data);
5325 switch (prop)
5326 {
5327 case 0:
5328 {
5329 sp_repr_css_set_property (css, "writing-mode", "lr");
5330 break;
5331 }
5333 case 1:
5334 {
5335 sp_repr_css_set_property (css, "writing-mode", "tb");
5336 break;
5337 }
5338 }
5340 SPStyle *query =
5341 sp_style_new (SP_ACTIVE_DOCUMENT);
5342 int result_numbers =
5343 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5345 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5346 if (result_numbers == QUERY_STYLE_NOTHING)
5347 {
5348 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5349 }
5351 sp_desktop_set_style (desktop, css, true, true);
5352 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5353 _("Text: Change orientation"));
5354 sp_repr_css_attr_unref (css);
5356 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5357 }
5359 gboolean
5360 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5361 {
5362 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5363 if (!desktop) return FALSE;
5365 switch (get_group0_keyval (event)) {
5366 case GDK_Escape: // defocus
5367 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5368 sp_text_toolbox_selection_changed (NULL, tbl); // update
5369 return TRUE; // I consumed the event
5370 break;
5371 }
5372 return FALSE;
5373 }
5375 gboolean
5376 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5377 {
5378 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5379 if (!desktop) return FALSE;
5381 switch (get_group0_keyval (event)) {
5382 case GDK_KP_Enter:
5383 case GDK_Return:
5384 case GDK_Escape: // defocus
5385 gtk_widget_hide (w);
5386 popdown_visible = false;
5387 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5388 return TRUE; // I consumed the event
5389 break;
5390 case GDK_w:
5391 case GDK_W:
5392 if (event->state & GDK_CONTROL_MASK) {
5393 gtk_widget_hide (w);
5394 popdown_visible = false;
5395 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5396 return TRUE; // I consumed the event
5397 }
5398 break;
5399 }
5400 return FALSE;
5401 }
5404 void
5405 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5406 GObject *tbl)
5407 {
5408 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5410 if (g_object_get_data (tbl, "size-block")) return;
5412 // If this is not from selecting a size in the list (in which case get_active will give the
5413 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5414 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5415 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5416 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5417 return;
5419 gchar *endptr;
5420 gdouble value = -1;
5421 char *text = gtk_combo_box_get_active_text (cbox);
5422 if (text) {
5423 value = g_strtod (text, &endptr);
5424 if (endptr == text) // conversion failed, non-numeric input
5425 value = -1;
5426 free (text);
5427 }
5428 if (value <= 0) {
5429 return; // could not parse value
5430 }
5432 SPCSSAttr *css = sp_repr_css_attr_new ();
5433 Inkscape::CSSOStringStream osfs;
5434 osfs << value;
5435 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5437 SPStyle *query =
5438 sp_style_new (SP_ACTIVE_DOCUMENT);
5439 int result_numbers =
5440 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5442 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5443 if (result_numbers == QUERY_STYLE_NOTHING)
5444 {
5445 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5446 }
5448 sp_style_unref(query);
5450 sp_desktop_set_style (desktop, css, true, true);
5451 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5452 _("Text: Change font size"));
5453 sp_repr_css_attr_unref (css);
5455 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5456 }
5458 gboolean
5459 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5460 {
5461 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5462 if (!desktop) return FALSE;
5464 if (!g_object_get_data (tbl, "esc-pressed")) {
5465 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5466 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5467 sp_text_toolbox_size_changed (cbox, tbl);
5468 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5469 }
5470 return FALSE; // I consumed the event
5471 }
5474 gboolean
5475 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5476 {
5477 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5478 if (!desktop) return FALSE;
5480 switch (get_group0_keyval (event)) {
5481 case GDK_Escape: // defocus
5482 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5483 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5484 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5485 return TRUE; // I consumed the event
5486 break;
5487 case GDK_Return: // defocus
5488 case GDK_KP_Enter:
5489 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5490 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5491 sp_text_toolbox_size_changed (cbox, tbl);
5492 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5493 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5494 return TRUE; // I consumed the event
5495 break;
5496 }
5497 return FALSE;
5498 }
5500 void
5501 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5502 GObject *tbl)
5503 {
5504 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5505 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5506 int x, y;
5508 if (!popdown_visible)
5509 {
5510 gdk_window_get_origin (widget->window, &x, &y);
5511 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5512 gtk_widget_show_all (popdown);
5513 //sp_transientize (popdown);
5515 gdk_pointer_grab (widget->window, TRUE,
5516 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5517 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5518 GDK_POINTER_MOTION_MASK),
5519 NULL, NULL, GDK_CURRENT_TIME);
5521 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5523 popdown_visible = true;
5524 }
5525 else
5526 {
5527 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5528 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5529 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5530 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5531 gtk_widget_hide (popdown);
5532 popdown_visible = false;
5533 }
5534 }
5536 gboolean
5537 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5538 GdkEventFocus */*event*/,
5539 GObject */*tbl*/)
5540 {
5541 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5542 return FALSE;
5543 }
5545 gboolean
5546 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5547 GdkEventFocus */*event*/,
5548 GObject */*tbl*/)
5549 {
5550 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5552 if (popdown_hasfocus) {
5553 gtk_widget_hide (popdown);
5554 popdown_hasfocus = false;
5555 popdown_visible = false;
5556 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5557 return TRUE;
5558 }
5559 return FALSE;
5560 }
5562 gboolean
5563 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5564 GdkEventFocus */*event*/,
5565 GObject */*tbl*/)
5566 {
5567 popdown_hasfocus = true;
5568 return TRUE;
5569 }
5572 void
5573 cell_data_func (GtkTreeViewColumn */*column*/,
5574 GtkCellRenderer *cell,
5575 GtkTreeModel *tree_model,
5576 GtkTreeIter *iter,
5577 gpointer /*data*/)
5578 {
5579 char *family,
5580 *family_escaped,
5581 *sample_escaped;
5583 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5585 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5587 family_escaped = g_markup_escape_text (family, -1);
5588 sample_escaped = g_markup_escape_text (sample, -1);
5590 std::stringstream markup;
5591 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5592 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5594 free (family);
5595 free (family_escaped);
5596 free (sample_escaped);
5597 }
5599 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5600 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5601 if (completion) {
5602 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5603 g_object_unref (completion);
5604 }
5605 }
5607 GtkWidget*
5608 sp_text_toolbox_new (SPDesktop *desktop)
5609 {
5610 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5611 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5613 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5614 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5616 GtkTooltips *tt = gtk_tooltips_new();
5617 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5619 ////////////Family
5620 //Window
5621 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5622 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5624 //Entry
5625 GtkWidget *entry = gtk_entry_new ();
5626 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5627 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5628 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5629 gtk_entry_completion_set_text_column (completion, 0);
5630 gtk_entry_completion_set_minimum_key_length (completion, 1);
5631 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5632 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5633 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5634 gtk_toolbar_append_widget( tbl, entry, "", "" );
5635 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5637 //Button
5638 GtkWidget *button = gtk_button_new ();
5639 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5640 gtk_toolbar_append_widget( tbl, button, "", "");
5642 //Popdown
5643 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5644 GtkWidget *treeview = gtk_tree_view_new ();
5646 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5647 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5648 gtk_tree_view_column_pack_start (column, cell, FALSE);
5649 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5650 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5651 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5653 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5654 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5655 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5657 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5659 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5660 gtk_container_add (GTK_CONTAINER (sw), treeview);
5662 gtk_container_add (GTK_CONTAINER (window), sw);
5663 gtk_widget_set_size_request (window, 300, 450);
5665 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5666 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5667 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5669 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5671 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5672 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5673 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5675 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5676 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5678 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5679 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5680 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5681 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5682 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5684 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5685 GtkWidget *box = gtk_event_box_new ();
5686 gtk_container_add (GTK_CONTAINER (box), image);
5687 gtk_toolbar_append_widget( tbl, box, "", "");
5688 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5689 GtkTooltips *tooltips = gtk_tooltips_new ();
5690 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5691 gtk_widget_hide (GTK_WIDGET (box));
5692 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5694 ////////////Size
5695 const char *sizes[] = {
5696 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5697 "16", "18", "20", "22", "24", "28",
5698 "32", "36", "40", "48", "56", "64", "72", "144"
5699 };
5701 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5702 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5703 gtk_widget_set_size_request (cbox, 80, -1);
5704 gtk_toolbar_append_widget( tbl, cbox, "", "");
5705 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5706 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5707 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5708 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5710 ////////////Text anchor
5711 GtkWidget *group = gtk_radio_button_new (NULL);
5712 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5713 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5715 // left
5716 GtkWidget *rbutton = group;
5717 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5718 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5719 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5721 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5722 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5723 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5724 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5726 // center
5727 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5728 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5729 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5730 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5732 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5733 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5734 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5735 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5737 // right
5738 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5739 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5740 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5741 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5743 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5744 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5745 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5746 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5748 // fill
5749 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5750 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5751 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5752 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5754 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5755 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5756 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5757 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5759 gtk_toolbar_append_widget( tbl, row, "", "");
5761 //spacer
5762 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5764 ////////////Text style
5765 row = gtk_hbox_new (FALSE, 4);
5767 // bold
5768 rbutton = gtk_toggle_button_new ();
5769 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5770 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5771 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5772 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5774 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5775 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5776 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5778 // italic
5779 rbutton = gtk_toggle_button_new ();
5780 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5781 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5782 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5783 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5785 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5786 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5787 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5789 gtk_toolbar_append_widget( tbl, row, "", "");
5791 //spacer
5792 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5794 ////////////Text orientation
5795 group = gtk_radio_button_new (NULL);
5796 row = gtk_hbox_new (FALSE, 4);
5797 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5799 // horizontal
5800 rbutton = group;
5801 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5802 gtk_container_add (GTK_CONTAINER (rbutton),
5803 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5804 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5805 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5807 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5808 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5809 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5811 // vertical
5812 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5813 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5814 gtk_container_add (GTK_CONTAINER (rbutton),
5815 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5816 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5817 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5819 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5820 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5821 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5822 gtk_toolbar_append_widget( tbl, row, "", "" );
5825 //watch selection
5826 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5828 sigc::connection *c_selection_changed =
5829 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5830 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5831 pool->add_connection ("selection-changed", c_selection_changed);
5833 sigc::connection *c_selection_modified =
5834 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5835 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5836 pool->add_connection ("selection-modified", c_selection_modified);
5838 sigc::connection *c_subselection_changed =
5839 new sigc::connection (desktop->connectToolSubselectionChanged
5840 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5841 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5843 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5846 gtk_widget_show_all( GTK_WIDGET(tbl) );
5848 return GTK_WIDGET(tbl);
5849 } // end of sp_text_toolbox_new()
5851 }//<unnamed> namespace
5854 //#########################
5855 //## Connector ##
5856 //#########################
5858 static void sp_connector_path_set_avoid(void)
5859 {
5860 cc_selection_set_avoid(true);
5861 }
5864 static void sp_connector_path_set_ignore(void)
5865 {
5866 cc_selection_set_avoid(false);
5867 }
5871 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5872 {
5873 // quit if run by the _changed callbacks
5874 if (g_object_get_data( tbl, "freeze" )) {
5875 return;
5876 }
5878 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5879 SPDocument *doc = sp_desktop_document(desktop);
5881 if (!sp_document_get_undo_sensitive(doc))
5882 {
5883 return;
5884 }
5886 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5888 if ( repr->attribute("inkscape:connector-spacing") ) {
5889 gdouble priorValue = gtk_adjustment_get_value(adj);
5890 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5891 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5892 return;
5893 }
5894 } else if ( adj->value == defaultConnSpacing ) {
5895 return;
5896 }
5898 // in turn, prevent callbacks from responding
5899 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5901 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5902 SP_OBJECT(desktop->namedview)->updateRepr();
5904 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5905 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5906 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5907 NR::Matrix m = NR::identity();
5908 avoid_item_move(&m, item);
5909 }
5911 if (items) {
5912 g_slist_free(items);
5913 }
5915 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5916 _("Change connector spacing"));
5918 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5920 spinbutton_defocus(GTK_OBJECT(tbl));
5921 }
5923 static void sp_connector_graph_layout(void)
5924 {
5925 if (!SP_ACTIVE_DESKTOP) return;
5927 // hack for clones, see comment in align-and-distribute.cpp
5928 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5929 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5931 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5933 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5935 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5936 }
5938 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5939 {
5940 if ( gtk_toggle_action_get_active( act ) ) {
5941 prefs_set_string_attribute("tools.connector", "directedlayout",
5942 "true");
5943 } else {
5944 prefs_set_string_attribute("tools.connector", "directedlayout",
5945 "false");
5946 }
5947 }
5949 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5950 {
5951 if ( gtk_toggle_action_get_active( act ) ) {
5952 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5953 "true");
5954 } else {
5955 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5956 "false");
5957 }
5958 }
5961 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5962 {
5963 prefs_set_double_attribute("tools.connector", "length", adj->value);
5964 spinbutton_defocus(GTK_OBJECT(tbl));
5965 }
5967 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5968 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5969 bool /*is_interactive*/, gpointer data)
5970 {
5971 GtkWidget *tbl = GTK_WIDGET(data);
5973 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5974 return;
5975 }
5976 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5977 return;
5978 }
5980 GtkAdjustment *adj = (GtkAdjustment*)
5981 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5982 gdouble spacing = defaultConnSpacing;
5983 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5985 gtk_adjustment_set_value(adj, spacing);
5986 }
5989 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5990 NULL, /* child_added */
5991 NULL, /* child_removed */
5992 connector_tb_event_attr_changed,
5993 NULL, /* content_changed */
5994 NULL /* order_changed */
5995 };
5998 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5999 {
6000 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6002 {
6003 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6004 _("Avoid"),
6005 _("Make connectors avoid selected objects"),
6006 "connector_avoid",
6007 secondarySize );
6008 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6009 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6010 }
6012 {
6013 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6014 _("Ignore"),
6015 _("Make connectors ignore selected objects"),
6016 "connector_ignore",
6017 secondarySize );
6018 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6019 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6020 }
6022 EgeAdjustmentAction* eact = 0;
6024 // Spacing spinbox
6025 eact = create_adjustment_action( "ConnectorSpacingAction",
6026 _("Connector Spacing"), _("Spacing:"),
6027 _("The amount of space left around objects by auto-routing connectors"),
6028 "tools.connector", "spacing", defaultConnSpacing,
6029 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6030 0, 100, 1.0, 10.0,
6031 0, 0, 0,
6032 connector_spacing_changed, 1, 0 );
6033 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6035 // Graph (connector network) layout
6036 {
6037 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6038 _("Graph"),
6039 _("Nicely arrange selected connector network"),
6040 "graph_layout",
6041 secondarySize );
6042 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6043 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6044 }
6046 // Default connector length spinbox
6047 eact = create_adjustment_action( "ConnectorLengthAction",
6048 _("Connector Length"), _("Length:"),
6049 _("Ideal length for connectors when layout is applied"),
6050 "tools.connector", "length", 100,
6051 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6052 10, 1000, 10.0, 100.0,
6053 0, 0, 0,
6054 connector_length_changed, 1, 0 );
6055 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6058 // Directed edges toggle button
6059 {
6060 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6061 _("Downwards"),
6062 _("Make connectors with end-markers (arrows) point downwards"),
6063 "directed_graph",
6064 Inkscape::ICON_SIZE_DECORATION );
6065 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6067 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6068 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6069 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6071 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6072 }
6074 // Avoid overlaps toggle button
6075 {
6076 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6077 _("Remove overlaps"),
6078 _("Do not allow overlapping shapes"),
6079 "remove_overlaps",
6080 Inkscape::ICON_SIZE_DECORATION );
6081 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6083 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6084 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6085 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6087 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6088 }
6090 // Code to watch for changes to the connector-spacing attribute in
6091 // the XML.
6092 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6093 g_assert(repr != NULL);
6095 purge_repr_listener( holder, holder );
6097 if (repr) {
6098 g_object_set_data( holder, "repr", repr );
6099 Inkscape::GC::anchor(repr);
6100 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6101 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6102 }
6103 } // end of sp_connector_toolbox_prep()
6106 //#########################
6107 //## Paintbucket ##
6108 //#########################
6110 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6111 {
6112 gint channels = ege_select_one_action_get_active( act );
6113 flood_channels_set_channels( channels );
6114 }
6116 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6117 {
6118 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6119 }
6121 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6122 {
6123 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6124 }
6126 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6127 {
6128 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6129 SPUnit const *unit = tracker->getActiveUnit();
6131 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6133 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6134 }
6136 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6137 {
6138 // FIXME: make defaults settable via Inkscape Options
6139 struct KeyValue {
6140 char const *key;
6141 double value;
6142 } const key_values[] = {
6143 {"threshold", 15},
6144 {"offset", 0.0}
6145 };
6147 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6148 KeyValue const &kv = key_values[i];
6149 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6150 if ( adj ) {
6151 gtk_adjustment_set_value(adj, kv.value);
6152 }
6153 }
6155 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6156 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6157 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6158 ege_select_one_action_set_active( autogap_action, 0 );
6159 }
6161 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6162 {
6163 EgeAdjustmentAction* eact = 0;
6165 {
6166 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6168 GList* items = 0;
6169 gint count = 0;
6170 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6171 {
6172 GtkTreeIter iter;
6173 gtk_list_store_append( model, &iter );
6174 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6175 count++;
6176 }
6177 g_list_free( items );
6178 items = 0;
6179 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6180 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6181 ege_select_one_action_set_appearance( act1, "compact" );
6182 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6183 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6184 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6185 g_object_set_data( holder, "channels_action", act1 );
6186 }
6188 // Spacing spinbox
6189 {
6190 eact = create_adjustment_action(
6191 "ThresholdAction",
6192 _("Fill Threshold"), _("Threshold:"),
6193 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6194 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6195 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6196 0, 0, 0,
6197 paintbucket_threshold_changed, 1, 0 );
6199 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6200 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6201 }
6203 // Create the units menu.
6204 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6205 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6206 if (stored_unit)
6207 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6208 g_object_set_data( holder, "tracker", tracker );
6209 {
6210 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6211 gtk_action_group_add_action( mainActions, act );
6212 }
6214 // Offset spinbox
6215 {
6216 eact = create_adjustment_action(
6217 "OffsetAction",
6218 _("Grow/shrink by"), _("Grow/shrink by:"),
6219 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6220 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6221 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6222 0, 0, 0,
6223 paintbucket_offset_changed, 1, 2);
6224 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6226 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6227 }
6229 /* Auto Gap */
6230 {
6231 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6233 GList* items = 0;
6234 gint count = 0;
6235 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6236 {
6237 GtkTreeIter iter;
6238 gtk_list_store_append( model, &iter );
6239 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6240 count++;
6241 }
6242 g_list_free( items );
6243 items = 0;
6244 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6245 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6246 ege_select_one_action_set_appearance( act2, "compact" );
6247 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6248 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6249 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6250 g_object_set_data( holder, "autogap_action", act2 );
6251 }
6253 /* Reset */
6254 {
6255 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6256 _("Defaults"),
6257 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6258 GTK_STOCK_CLEAR );
6259 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6260 gtk_action_group_add_action( mainActions, act );
6261 gtk_action_set_sensitive( act, TRUE );
6262 }
6264 }
6266 /*
6267 Local Variables:
6268 mode:c++
6269 c-file-style:"stroustrup"
6270 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6271 indent-tabs-mode:nil
6272 fill-column:99
6273 End:
6274 */
6275 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :