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 "pen-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 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3272 static char const *
3273 freehand_tool_name(GObject *dataKludge)
3274 {
3275 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3276 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3277 ? "tools.freehand.pen"
3278 : "tools.freehand.pencil" );
3279 }
3281 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3282 {
3283 gint mode = ege_select_one_action_get_active(act);
3285 prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3287 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3289 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3290 // preparatory work here
3291 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3292 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3293 pc->polylines_only = (mode == 2);
3294 }
3295 }
3297 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3298 {
3299 /* Freehand mode toggle buttons */
3300 {
3301 guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3302 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3304 {
3305 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3307 GtkTreeIter iter;
3308 gtk_list_store_append( model, &iter );
3309 gtk_list_store_set( model, &iter,
3310 0, _("Bezier"),
3311 1, _("Create regular Bezier path"),
3312 2, "bezier_mode",
3313 -1 );
3315 gtk_list_store_append( model, &iter );
3316 gtk_list_store_set( model, &iter,
3317 0, _("Spiro"),
3318 1, _("Create Spiro path"),
3319 2, "spiro_splines_mode",
3320 -1 );
3322 if (!tool_is_pencil) {
3323 gtk_list_store_append( model, &iter );
3324 gtk_list_store_set( model, &iter,
3325 0, _("Zigzag"),
3326 1, _("Create a sequence of straight line segments"),
3327 2, "polylines_mode",
3328 -1 );
3329 }
3331 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3332 "FreehandModeActionPencil" :
3333 "FreehandModeActionPen",
3334 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3335 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3337 ege_select_one_action_set_appearance( act, "full" );
3338 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3339 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3340 ege_select_one_action_set_icon_column( act, 2 );
3341 ege_select_one_action_set_icon_size( act, secondarySize );
3342 ege_select_one_action_set_tooltip_column( act, 1 );
3344 ege_select_one_action_set_active( act, freehandMode);
3345 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3346 }
3347 }
3348 }
3350 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3351 gint shape = ege_select_one_action_get_active( act );
3352 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3353 }
3355 /**
3356 * \brief Generate the list of freehand advanced shape option entries.
3357 */
3358 GList * freehand_shape_dropdown_items_list() {
3359 GList *glist = NULL;
3361 glist = g_list_append (glist, _("None"));
3362 glist = g_list_append (glist, _("Triangle in"));
3363 glist = g_list_append (glist, _("Triangle out"));
3364 glist = g_list_append (glist, _("Ellipse"));
3365 glist = g_list_append (glist, _("From clipboard"));
3367 return glist;
3368 }
3370 static void
3371 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3372 /*advanced shape options */
3373 {
3374 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3376 GList* items = 0;
3377 gint count = 0;
3378 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3379 {
3380 GtkTreeIter iter;
3381 gtk_list_store_append( model, &iter );
3382 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3383 count++;
3384 }
3385 g_list_free( items );
3386 items = 0;
3387 EgeSelectOneAction* act1 = ege_select_one_action_new(
3388 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3389 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3390 g_object_set( act1, "short_label", _("Shape:"), NULL );
3391 ege_select_one_action_set_appearance( act1, "compact" );
3392 ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3393 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3394 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3395 g_object_set_data( holder, "shape_action", act1 );
3396 }
3397 }
3399 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3400 {
3401 sp_add_freehand_mode_toggle(mainActions, holder, false);
3402 freehand_add_advanced_shape_options(mainActions, holder, false);
3403 }
3406 static void
3407 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3408 {
3409 GtkWidget *tbl = GTK_WIDGET(obj);
3411 GtkAdjustment *adj;
3413 // fixme: make settable
3414 gdouble tolerance = 4;
3416 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3417 gtk_adjustment_set_value(adj, tolerance);
3418 gtk_adjustment_value_changed(adj);
3420 spinbutton_defocus(GTK_OBJECT(tbl));
3421 }
3423 static void
3424 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3425 {
3427 // quit if run by the attr_changed listener
3428 if (g_object_get_data( tbl, "freeze" )) {
3429 return;
3430 }
3431 // in turn, prevent listener from responding
3432 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3433 prefs_set_double_attribute("tools.freehand.pencil",
3434 "tolerance", adj->value);
3435 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3437 }
3441 static void
3442 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3443 const gchar */*key*/,
3444 const gchar */*oldval*/,
3445 const gchar */*newval*/,
3446 bool /*is_interactive*/,
3447 void * data)
3448 {
3449 GObject* tbl = G_OBJECT(data);
3450 if (g_object_get_data( tbl, "freeze" )) {
3451 return;
3452 }
3454 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3456 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3457 "tolerance");
3459 double v = prefs_get_double_attribute("tools.freehand.pencil",
3460 "tolerance", adj->value);
3461 gtk_adjustment_set_value(adj, v);
3462 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3464 }
3466 static Inkscape::XML::NodeEventVector pencil_node_events =
3467 {
3468 NULL,
3469 NULL,
3470 sp_pencil_tb_tolerance_value_changed_external,
3471 NULL,
3472 NULL,
3473 };
3476 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3477 {
3478 sp_add_freehand_mode_toggle(mainActions, holder, true);
3480 EgeAdjustmentAction* eact = 0;
3482 /* Tolerance */
3483 {
3484 gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3485 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3486 eact = create_adjustment_action( "PencilToleranceAction",
3487 _("Smoothing:"), _("Smoothing: "),
3488 _("How much smoothing (simplifying) is applied to the line"),
3489 "tools.freehand.pencil", "tolerance",
3490 3.0,
3491 GTK_WIDGET(desktop->canvas), NULL,
3492 holder, TRUE, "altx-pencil",
3493 1, 100.0, 0.5, 0,
3494 labels, values, G_N_ELEMENTS(labels),
3495 sp_pencil_tb_tolerance_value_changed,
3496 1, 2);
3497 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3498 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3500 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3501 "tools.freehand.pencil");
3502 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3503 g_object_set_data(G_OBJECT(holder), "repr", repr);
3505 }
3507 /* advanced shape options */
3508 freehand_add_advanced_shape_options(mainActions, holder, true);
3510 /* Reset */
3511 {
3512 InkAction* inky = ink_action_new( "PencilResetAction",
3513 _("Defaults"),
3514 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3515 GTK_STOCK_CLEAR,
3516 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3517 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3518 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3519 }
3521 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3523 }
3526 //########################
3527 //## Tweak ##
3528 //########################
3530 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3531 {
3532 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3533 }
3535 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3536 {
3537 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3538 }
3540 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3541 {
3542 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3543 }
3545 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3546 {
3547 int mode = ege_select_one_action_get_active( act );
3548 prefs_set_int_attribute("tools.tweak", "mode", mode);
3550 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3551 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3552 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3553 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3554 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3555 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3556 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3557 if (doh) gtk_action_set_sensitive (doh, TRUE);
3558 if (dos) gtk_action_set_sensitive (dos, TRUE);
3559 if (dol) gtk_action_set_sensitive (dol, TRUE);
3560 if (doo) gtk_action_set_sensitive (doo, TRUE);
3561 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3562 if (fid) gtk_action_set_sensitive (fid, FALSE);
3563 } else {
3564 if (doh) gtk_action_set_sensitive (doh, FALSE);
3565 if (dos) gtk_action_set_sensitive (dos, FALSE);
3566 if (dol) gtk_action_set_sensitive (dol, FALSE);
3567 if (doo) gtk_action_set_sensitive (doo, FALSE);
3568 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3569 if (fid) gtk_action_set_sensitive (fid, TRUE);
3570 }
3571 }
3573 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3574 {
3575 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3576 }
3578 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3579 bool show = gtk_toggle_action_get_active( act );
3580 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3581 }
3582 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3583 bool show = gtk_toggle_action_get_active( act );
3584 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3585 }
3586 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3587 bool show = gtk_toggle_action_get_active( act );
3588 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3589 }
3590 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3591 bool show = gtk_toggle_action_get_active( act );
3592 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3593 }
3595 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3596 {
3597 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3599 {
3600 /* Width */
3601 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3602 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3603 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3604 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3605 "tools.tweak", "width", 15,
3606 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3607 1, 100, 1.0, 0.0,
3608 labels, values, G_N_ELEMENTS(labels),
3609 sp_tweak_width_value_changed, 0.01, 0, 100 );
3610 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3611 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3612 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3613 }
3616 {
3617 /* Force */
3618 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3619 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3620 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3621 _("Force"), _("Force:"), _("The force of the tweak action"),
3622 "tools.tweak", "force", 20,
3623 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3624 1, 100, 1.0, 0.0,
3625 labels, values, G_N_ELEMENTS(labels),
3626 sp_tweak_force_value_changed, 0.01, 0, 100 );
3627 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3628 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3629 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3630 }
3632 /* Mode */
3633 {
3634 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3636 GtkTreeIter iter;
3637 gtk_list_store_append( model, &iter );
3638 gtk_list_store_set( model, &iter,
3639 0, _("Push mode"),
3640 1, _("Push parts of paths in any direction"),
3641 2, "tweak_push_mode",
3642 -1 );
3644 gtk_list_store_append( model, &iter );
3645 gtk_list_store_set( model, &iter,
3646 0, _("Shrink mode"),
3647 1, _("Shrink (inset) parts of paths"),
3648 2, "tweak_shrink_mode",
3649 -1 );
3651 gtk_list_store_append( model, &iter );
3652 gtk_list_store_set( model, &iter,
3653 0, _("Grow mode"),
3654 1, _("Grow (outset) parts of paths"),
3655 2, "tweak_grow_mode",
3656 -1 );
3658 gtk_list_store_append( model, &iter );
3659 gtk_list_store_set( model, &iter,
3660 0, _("Attract mode"),
3661 1, _("Attract parts of paths towards cursor"),
3662 2, "tweak_attract_mode",
3663 -1 );
3665 gtk_list_store_append( model, &iter );
3666 gtk_list_store_set( model, &iter,
3667 0, _("Repel mode"),
3668 1, _("Repel parts of paths from cursor"),
3669 2, "tweak_repel_mode",
3670 -1 );
3672 gtk_list_store_append( model, &iter );
3673 gtk_list_store_set( model, &iter,
3674 0, _("Roughen mode"),
3675 1, _("Roughen parts of paths"),
3676 2, "tweak_roughen_mode",
3677 -1 );
3679 gtk_list_store_append( model, &iter );
3680 gtk_list_store_set( model, &iter,
3681 0, _("Color paint mode"),
3682 1, _("Paint the tool's color upon selected objects"),
3683 2, "tweak_colorpaint_mode",
3684 -1 );
3686 gtk_list_store_append( model, &iter );
3687 gtk_list_store_set( model, &iter,
3688 0, _("Color jitter mode"),
3689 1, _("Jitter the colors of selected objects"),
3690 2, "tweak_colorjitter_mode",
3691 -1 );
3693 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3694 g_object_set( act, "short_label", _("Mode:"), NULL );
3695 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3696 g_object_set_data( holder, "mode_action", act );
3698 ege_select_one_action_set_appearance( act, "full" );
3699 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3700 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3701 ege_select_one_action_set_icon_column( act, 2 );
3702 ege_select_one_action_set_icon_size( act, secondarySize );
3703 ege_select_one_action_set_tooltip_column( act, 1 );
3705 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3706 ege_select_one_action_set_active( act, mode );
3707 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3709 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3710 }
3712 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3714 {
3715 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3716 ege_output_action_set_use_markup( act, TRUE );
3717 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3718 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3719 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3720 g_object_set_data( holder, "tweak_channels_label", act);
3721 }
3723 {
3724 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3725 _("Hue"),
3726 _("In color mode, act on objects' hue"),
3727 NULL,
3728 Inkscape::ICON_SIZE_DECORATION );
3729 //TRANSLATORS: "H" here stands for hue
3730 g_object_set( act, "short_label", _("H"), NULL );
3731 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3732 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3733 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3734 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3735 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3736 g_object_set_data( holder, "tweak_doh", act);
3737 }
3738 {
3739 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3740 _("Saturation"),
3741 _("In color mode, act on objects' saturation"),
3742 NULL,
3743 Inkscape::ICON_SIZE_DECORATION );
3744 //TRANSLATORS: "S" here stands for Saturation
3745 g_object_set( act, "short_label", _("S"), NULL );
3746 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3747 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3748 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3749 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3750 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3751 g_object_set_data( holder, "tweak_dos", act );
3752 }
3753 {
3754 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3755 _("Lightness"),
3756 _("In color mode, act on objects' lightness"),
3757 NULL,
3758 Inkscape::ICON_SIZE_DECORATION );
3759 //TRANSLATORS: "L" here stands for Lightness
3760 g_object_set( act, "short_label", _("L"), NULL );
3761 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3762 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3763 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3764 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3765 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3766 g_object_set_data( holder, "tweak_dol", act );
3767 }
3768 {
3769 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3770 _("Opacity"),
3771 _("In color mode, act on objects' opacity"),
3772 NULL,
3773 Inkscape::ICON_SIZE_DECORATION );
3774 //TRANSLATORS: "O" here stands for Opacity
3775 g_object_set( act, "short_label", _("O"), NULL );
3776 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3777 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3778 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3779 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3780 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3781 g_object_set_data( holder, "tweak_doo", act );
3782 }
3784 { /* Fidelity */
3785 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3786 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3787 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3788 _("Fidelity"), _("Fidelity:"),
3789 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3790 "tools.tweak", "fidelity", 50,
3791 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3792 1, 100, 1.0, 10.0,
3793 labels, values, G_N_ELEMENTS(labels),
3794 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3795 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3796 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3797 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3798 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3799 g_object_set_data( holder, "tweak_fidelity", eact );
3800 }
3803 /* Use Pressure button */
3804 {
3805 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3806 _("Pressure"),
3807 _("Use the pressure of the input device to alter the force of tweak action"),
3808 "use_pressure",
3809 Inkscape::ICON_SIZE_DECORATION );
3810 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3811 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3812 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3813 }
3815 }
3818 //########################
3819 //## Calligraphy ##
3820 //########################
3821 static void update_presets_list (GObject *tbl)
3822 {
3823 if (g_object_get_data(tbl, "presets_blocked"))
3824 return;
3826 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3827 if (!sel) {
3828 ege_select_one_action_set_active(sel, 0);
3829 return;
3830 }
3832 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3834 for (int i = 1; i <= total_prefs; i++) {
3835 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3836 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3838 bool match = true;
3840 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3841 iter;
3842 ++iter ) {
3843 const gchar *attr_name = g_quark_to_string(iter->key);
3844 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3845 continue;
3846 void *widget = g_object_get_data(tbl, attr_name);
3847 if (widget) {
3848 if (GTK_IS_ADJUSTMENT(widget)) {
3849 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3850 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3851 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3852 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3853 match = false;
3854 break;
3855 }
3856 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3857 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3858 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3859 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3860 if (gtk_toggle_action_get_active(toggle) != v) {
3861 match = false;
3862 break;
3863 }
3864 }
3865 }
3866 }
3868 if (match) {
3869 // newly added item is at the same index as the
3870 // save command, so we need to change twice for it to take effect
3871 ege_select_one_action_set_active(sel, 0);
3872 ege_select_one_action_set_active(sel, i);
3873 return;
3874 }
3875 }
3877 // no match found
3878 ege_select_one_action_set_active(sel, 0);
3879 }
3881 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3882 {
3883 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3884 update_presets_list(tbl);
3885 }
3887 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3888 {
3889 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3890 update_presets_list(tbl);
3891 }
3893 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3894 {
3895 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3896 update_presets_list(tbl);
3897 }
3899 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3900 {
3901 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3902 update_presets_list(tbl);
3903 }
3905 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3906 {
3907 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3908 update_presets_list(tbl);
3909 }
3911 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3912 {
3913 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3914 update_presets_list(tbl);
3915 }
3917 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3918 {
3919 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3920 update_presets_list(tbl);
3921 }
3923 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3924 {
3925 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3926 update_presets_list(tbl);
3927 }
3929 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3930 {
3931 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3932 update_presets_list(tbl);
3933 }
3935 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3936 {
3937 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3938 update_presets_list(tbl);
3939 }
3941 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3942 {
3943 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3944 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3945 update_presets_list(tbl);
3946 if (calligraphy_angle )
3947 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3948 }
3951 static gchar const *const widget_names[] = {
3952 "width",
3953 "mass",
3954 "wiggle",
3955 "angle",
3956 "thinning",
3957 "tremor",
3958 "flatness",
3959 "cap_rounding",
3960 "usepressure",
3961 "tracebackground",
3962 "usetilt"
3963 };
3966 static void sp_dcc_build_presets_list(GObject *tbl)
3967 {
3968 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3970 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3971 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3972 gtk_list_store_clear (model);
3974 {
3975 GtkTreeIter iter;
3976 gtk_list_store_append( model, &iter );
3977 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3978 }
3980 //TODO: switch back to prefs API
3981 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3982 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3983 int ii=1;
3984 while (child_repr) {
3985 GtkTreeIter iter;
3986 char *preset_name = (char *) child_repr->attribute("name");
3987 gtk_list_store_append( model, &iter );
3988 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3989 child_repr = sp_repr_next(child_repr);
3990 }
3992 {
3993 GtkTreeIter iter;
3994 gtk_list_store_append( model, &iter );
3995 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
3996 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
3997 }
3999 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4001 update_presets_list (tbl);
4002 }
4004 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4005 {
4006 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4007 if (! desktop) return;
4009 if (g_object_get_data(tbl, "presets_blocked"))
4010 return;
4012 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4013 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4014 // dialog cancelled
4015 update_presets_list (tbl);
4016 return;
4017 }
4018 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4020 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4021 // empty name entered
4022 update_presets_list (tbl);
4023 return;
4024 }
4026 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4028 int new_index = -1;
4029 gchar *pref_path = NULL;
4030 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4032 for (int i = 1; i <= total_prefs; i++) {
4033 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4034 const gchar *name = prefs_get_string_attribute(path, "name");
4035 if (name && !strcmp(name, profile_name.c_str())) {
4036 // we already have preset with this name, replace its values
4037 new_index = i;
4038 pref_path = g_strdup(path);
4039 break;
4040 }
4041 }
4043 if (new_index == -1) {
4044 // no preset with this name, create
4045 new_index = total_prefs + 1;
4046 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4047 pref_path = create_pref("tools.calligraphic.preset", profile_id);
4048 g_free(profile_id);
4049 }
4051 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4052 gchar const *const widget_name = widget_names[i];
4053 void *widget = g_object_get_data(tbl, widget_name);
4054 if (widget) {
4055 if (GTK_IS_ADJUSTMENT(widget)) {
4056 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4057 double v = gtk_adjustment_get_value(adj);
4058 prefs_set_double_attribute(pref_path, widget_name, v);
4059 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4060 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4061 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4062 int v = gtk_toggle_action_get_active(toggle);
4063 prefs_set_int_attribute(pref_path, widget_name, v);
4064 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4065 } else {
4066 g_warning("Unknown widget type for preset: %s\n", widget_name);
4067 }
4068 } else {
4069 g_warning("Bad key when writing preset: %s\n", widget_name);
4070 }
4071 }
4072 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4074 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4076 sp_dcc_build_presets_list (tbl);
4078 g_free(pref_path);
4079 }
4082 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4084 gint preset_index = ege_select_one_action_get_active( act );
4085 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4087 if (preset_index == save_presets_index) {
4088 // this is the Save command
4089 sp_dcc_save_profile(NULL, tbl);
4090 return;
4091 }
4093 if (g_object_get_data(tbl, "presets_blocked"))
4094 return;
4096 gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4098 if (preset_path) {
4099 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
4101 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4103 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4104 iter;
4105 ++iter ) {
4106 const gchar *attr_name = g_quark_to_string(iter->key);
4107 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4108 continue;
4109 void *widget = g_object_get_data(tbl, attr_name);
4110 if (widget) {
4111 if (GTK_IS_ADJUSTMENT(widget)) {
4112 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4113 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4114 gtk_adjustment_set_value(adj, v);
4115 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4116 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4117 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4118 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4119 gtk_toggle_action_set_active(toggle, v);
4120 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4121 } else {
4122 g_warning("Unknown widget type for preset: %s\n", attr_name);
4123 }
4124 } else {
4125 g_warning("Bad key found in a preset record: %s\n", attr_name);
4126 }
4127 }
4128 g_free(preset_path);
4129 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4130 }
4132 }
4135 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4136 {
4137 {
4138 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4140 EgeAdjustmentAction* calligraphy_angle = 0;
4142 {
4143 /* Width */
4144 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4145 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4146 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4147 _("Pen Width"), _("Width:"),
4148 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4149 "tools.calligraphic", "width", 15,
4150 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4151 1, 100, 1.0, 0.0,
4152 labels, values, G_N_ELEMENTS(labels),
4153 sp_ddc_width_value_changed, 0.01, 0, 100 );
4154 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4155 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4156 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4157 }
4159 {
4160 /* Thinning */
4161 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4162 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4163 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4164 _("Stroke Thinning"), _("Thinning:"),
4165 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4166 "tools.calligraphic", "thinning", 10,
4167 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4168 -100, 100, 1, 0.1,
4169 labels, values, G_N_ELEMENTS(labels),
4170 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4171 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4172 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4173 }
4175 {
4176 /* Angle */
4177 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4178 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4179 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4180 _("Pen Angle"), _("Angle:"),
4181 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4182 "tools.calligraphic", "angle", 30,
4183 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4184 -90.0, 90.0, 1.0, 10.0,
4185 labels, values, G_N_ELEMENTS(labels),
4186 sp_ddc_angle_value_changed, 1, 0 );
4187 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4188 g_object_set_data( holder, "angle_action", eact );
4189 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4190 calligraphy_angle = eact;
4191 }
4193 {
4194 /* Fixation */
4195 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4196 gdouble values[] = {0, 20, 40, 60, 90, 100};
4197 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4198 _("Fixation"), _("Fixation:"),
4199 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4200 "tools.calligraphic", "flatness", 90,
4201 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4202 0.0, 100, 1.0, 10.0,
4203 labels, values, G_N_ELEMENTS(labels),
4204 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4205 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4206 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4207 }
4209 {
4210 /* Cap Rounding */
4211 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4212 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4213 // TRANSLATORS: "cap" means "end" (both start and finish) here
4214 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4215 _("Cap rounding"), _("Caps:"),
4216 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4217 "tools.calligraphic", "cap_rounding", 0.0,
4218 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4219 0.0, 5.0, 0.01, 0.1,
4220 labels, values, G_N_ELEMENTS(labels),
4221 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4222 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4223 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4224 }
4226 {
4227 /* Tremor */
4228 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4229 gdouble values[] = {0, 10, 20, 40, 60, 100};
4230 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4231 _("Stroke Tremor"), _("Tremor:"),
4232 _("Increase to make strokes rugged and trembling"),
4233 "tools.calligraphic", "tremor", 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_tremor_value_changed, 0.01, 0, 100 );
4239 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4240 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4241 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4242 }
4244 {
4245 /* Wiggle */
4246 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4247 gdouble values[] = {0, 20, 40, 60, 100};
4248 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4249 _("Pen Wiggle"), _("Wiggle:"),
4250 _("Increase to make the pen waver and wiggle"),
4251 "tools.calligraphic", "wiggle", 0.0,
4252 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4253 0.0, 100, 1, 0.0,
4254 labels, values, G_N_ELEMENTS(labels),
4255 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4256 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4257 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4258 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4259 }
4261 {
4262 /* Mass */
4263 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4264 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4265 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4266 _("Pen Mass"), _("Mass:"),
4267 _("Increase to make the pen drag behind, as if slowed by inertia"),
4268 "tools.calligraphic", "mass", 2.0,
4269 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4270 0.0, 100, 1, 0.0,
4271 labels, values, G_N_ELEMENTS(labels),
4272 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4273 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4274 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4275 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4276 }
4279 /* Trace Background button */
4280 {
4281 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4282 _("Trace Background"),
4283 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4284 "trace_background",
4285 Inkscape::ICON_SIZE_DECORATION );
4286 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4287 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4288 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4289 g_object_set_data( holder, "tracebackground", act );
4290 }
4292 /* Use Pressure button */
4293 {
4294 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4295 _("Pressure"),
4296 _("Use the pressure of the input device to alter the width of the pen"),
4297 "use_pressure",
4298 Inkscape::ICON_SIZE_DECORATION );
4299 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4300 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4301 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4302 g_object_set_data( holder, "usepressure", act );
4303 }
4305 /* Use Tilt button */
4306 {
4307 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4308 _("Tilt"),
4309 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4310 "use_tilt",
4311 Inkscape::ICON_SIZE_DECORATION );
4312 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4313 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4314 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4315 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4316 g_object_set_data( holder, "usetilt", act );
4317 }
4319 /*calligraphic profile */
4320 {
4321 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4322 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4323 ege_select_one_action_set_appearance (act1, "compact");
4324 g_object_set_data (holder, "profile_selector", act1 );
4326 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4328 sp_dcc_build_presets_list (holder);
4330 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4331 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4332 }
4333 }
4334 }
4337 //########################
4338 //## Circle / Arc ##
4339 //########################
4341 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4342 {
4343 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4344 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4346 if (v1 == 0 && v2 == 0) {
4347 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4348 gtk_action_set_sensitive( ocb, FALSE );
4349 gtk_action_set_sensitive( make_whole, FALSE );
4350 }
4351 } else {
4352 gtk_action_set_sensitive( ocb, TRUE );
4353 gtk_action_set_sensitive( make_whole, TRUE );
4354 }
4355 }
4357 static void
4358 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4359 {
4360 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4362 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4363 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4364 }
4366 // quit if run by the attr_changed listener
4367 if (g_object_get_data( tbl, "freeze" )) {
4368 return;
4369 }
4371 // in turn, prevent listener from responding
4372 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4374 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4376 bool modmade = false;
4377 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4378 items != NULL;
4379 items = items->next)
4380 {
4381 SPItem *item = SP_ITEM(items->data);
4383 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4385 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4386 SPArc *arc = SP_ARC(item);
4388 if (!strcmp(value_name, "start"))
4389 ge->start = (adj->value * M_PI)/ 180;
4390 else
4391 ge->end = (adj->value * M_PI)/ 180;
4393 sp_genericellipse_normalize(ge);
4394 ((SPObject *)arc)->updateRepr();
4395 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4397 modmade = true;
4398 }
4399 }
4401 g_free(namespaced_name);
4403 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4405 sp_arctb_sensitivize( tbl, adj->value, other->value );
4407 if (modmade) {
4408 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4409 _("Arc: Change start/end"));
4410 }
4412 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4413 }
4416 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4417 {
4418 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4419 }
4421 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4422 {
4423 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4424 }
4427 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4428 {
4429 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4430 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4431 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4432 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4433 }
4435 // only take action if run by the attr_changed listener
4436 if (!g_object_get_data( tbl, "freeze" )) {
4437 // in turn, prevent listener from responding
4438 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4440 if ( eraserMode != 0 ) {
4441 } else {
4442 }
4443 // TODO finish implementation
4445 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4446 }
4447 }
4449 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4450 {
4451 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4452 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4453 if ( ege_select_one_action_get_active( act ) != 0 ) {
4454 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4455 } else {
4456 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4457 }
4458 }
4460 // quit if run by the attr_changed listener
4461 if (g_object_get_data( tbl, "freeze" )) {
4462 return;
4463 }
4465 // in turn, prevent listener from responding
4466 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4468 bool modmade = false;
4470 if ( ege_select_one_action_get_active(act) != 0 ) {
4471 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4472 items != NULL;
4473 items = items->next)
4474 {
4475 if (SP_IS_ARC((SPItem *) items->data)) {
4476 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4477 repr->setAttribute("sodipodi:open", "true");
4478 SP_OBJECT((SPItem *) items->data)->updateRepr();
4479 modmade = true;
4480 }
4481 }
4482 } else {
4483 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4484 items != NULL;
4485 items = items->next)
4486 {
4487 if (SP_IS_ARC((SPItem *) items->data)) {
4488 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4489 repr->setAttribute("sodipodi:open", NULL);
4490 SP_OBJECT((SPItem *) items->data)->updateRepr();
4491 modmade = true;
4492 }
4493 }
4494 }
4496 if (modmade) {
4497 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4498 _("Arc: Change open/closed"));
4499 }
4501 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4502 }
4504 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4505 {
4506 GtkAdjustment *adj;
4507 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4508 gtk_adjustment_set_value(adj, 0.0);
4509 gtk_adjustment_value_changed(adj);
4511 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4512 gtk_adjustment_set_value(adj, 0.0);
4513 gtk_adjustment_value_changed(adj);
4515 spinbutton_defocus( GTK_OBJECT(obj) );
4516 }
4518 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4519 gchar const */*old_value*/, gchar const */*new_value*/,
4520 bool /*is_interactive*/, gpointer data)
4521 {
4522 GObject *tbl = G_OBJECT(data);
4524 // quit if run by the _changed callbacks
4525 if (g_object_get_data( tbl, "freeze" )) {
4526 return;
4527 }
4529 // in turn, prevent callbacks from responding
4530 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4532 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4533 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4535 GtkAdjustment *adj1,*adj2;
4536 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4537 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4538 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4539 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4541 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4543 char const *openstr = NULL;
4544 openstr = repr->attribute("sodipodi:open");
4545 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4547 if (openstr) {
4548 ege_select_one_action_set_active( ocb, 1 );
4549 } else {
4550 ege_select_one_action_set_active( ocb, 0 );
4551 }
4553 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4554 }
4556 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4557 NULL, /* child_added */
4558 NULL, /* child_removed */
4559 arc_tb_event_attr_changed,
4560 NULL, /* content_changed */
4561 NULL /* order_changed */
4562 };
4565 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4566 {
4567 int n_selected = 0;
4568 Inkscape::XML::Node *repr = NULL;
4570 purge_repr_listener( tbl, tbl );
4572 for (GSList const *items = selection->itemList();
4573 items != NULL;
4574 items = items->next)
4575 {
4576 if (SP_IS_ARC((SPItem *) items->data)) {
4577 n_selected++;
4578 repr = SP_OBJECT_REPR((SPItem *) items->data);
4579 }
4580 }
4582 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4584 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4585 if (n_selected == 0) {
4586 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4587 } else if (n_selected == 1) {
4588 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4589 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4591 if (repr) {
4592 g_object_set_data( tbl, "repr", repr );
4593 Inkscape::GC::anchor(repr);
4594 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4595 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4596 }
4597 } else {
4598 // FIXME: implement averaging of all parameters for multiple selected
4599 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4600 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4601 sp_arctb_sensitivize( tbl, 1, 0 );
4602 }
4603 }
4606 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4607 {
4608 EgeAdjustmentAction* eact = 0;
4609 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4612 {
4613 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4614 ege_output_action_set_use_markup( act, TRUE );
4615 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4616 g_object_set_data( holder, "mode_action", act );
4617 }
4619 /* Start */
4620 {
4621 eact = create_adjustment_action( "ArcStartAction",
4622 _("Start"), _("Start:"),
4623 _("The angle (in degrees) from the horizontal to the arc's start point"),
4624 "tools.shapes.arc", "start", 0.0,
4625 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4626 -360.0, 360.0, 1.0, 10.0,
4627 0, 0, 0,
4628 sp_arctb_start_value_changed);
4629 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4630 }
4632 /* End */
4633 {
4634 eact = create_adjustment_action( "ArcEndAction",
4635 _("End"), _("End:"),
4636 _("The angle (in degrees) from the horizontal to the arc's end point"),
4637 "tools.shapes.arc", "end", 0.0,
4638 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4639 -360.0, 360.0, 1.0, 10.0,
4640 0, 0, 0,
4641 sp_arctb_end_value_changed);
4642 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4643 }
4645 /* Segments / Pie checkbox */
4646 {
4647 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4649 GtkTreeIter iter;
4650 gtk_list_store_append( model, &iter );
4651 gtk_list_store_set( model, &iter,
4652 0, _("Closed arc"),
4653 1, _("Switch to segment (closed shape with two radii)"),
4654 2, "circle_closed_arc",
4655 -1 );
4657 gtk_list_store_append( model, &iter );
4658 gtk_list_store_set( model, &iter,
4659 0, _("Open Arc"),
4660 1, _("Switch to arc (unclosed shape)"),
4661 2, "circle_open_arc",
4662 -1 );
4664 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4665 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4666 g_object_set_data( holder, "open_action", act );
4668 ege_select_one_action_set_appearance( act, "full" );
4669 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4670 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4671 ege_select_one_action_set_icon_column( act, 2 );
4672 ege_select_one_action_set_icon_size( act, secondarySize );
4673 ege_select_one_action_set_tooltip_column( act, 1 );
4675 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4676 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4677 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4678 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4679 }
4681 /* Make Whole */
4682 {
4683 InkAction* inky = ink_action_new( "ArcResetAction",
4684 _("Make whole"),
4685 _("Make the shape a whole ellipse, not arc or segment"),
4686 "reset_circle",
4687 secondarySize );
4688 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4689 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4690 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4691 g_object_set_data( holder, "make_whole", inky );
4692 }
4694 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4695 // sensitivize make whole and open checkbox
4696 {
4697 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4698 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4699 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4700 }
4703 sigc::connection *connection = new sigc::connection(
4704 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4705 );
4706 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4707 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4708 }
4713 // toggle button callbacks and updaters
4715 //########################
4716 //## Dropper ##
4717 //########################
4719 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4720 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4721 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4722 if ( set_action ) {
4723 if ( gtk_toggle_action_get_active( act ) ) {
4724 gtk_action_set_sensitive( set_action, TRUE );
4725 } else {
4726 gtk_action_set_sensitive( set_action, FALSE );
4727 }
4728 }
4730 spinbutton_defocus(GTK_OBJECT(tbl));
4731 }
4733 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4734 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4735 spinbutton_defocus(GTK_OBJECT(tbl));
4736 }
4739 /**
4740 * Dropper auxiliary toolbar construction and setup.
4741 *
4742 * TODO: Would like to add swatch of current color.
4743 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4744 * can drag and drop places. Will provide a nice mixing palette.
4745 */
4746 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4747 {
4748 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4750 {
4751 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4752 ege_output_action_set_use_markup( act, TRUE );
4753 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4754 }
4756 {
4757 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4758 _("Pick opacity"),
4759 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4760 NULL,
4761 Inkscape::ICON_SIZE_DECORATION );
4762 g_object_set( act, "short_label", _("Pick"), NULL );
4763 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4764 g_object_set_data( holder, "pick_action", act );
4765 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4766 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4767 }
4769 {
4770 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4771 _("Assign opacity"),
4772 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4773 NULL,
4774 Inkscape::ICON_SIZE_DECORATION );
4775 g_object_set( act, "short_label", _("Assign"), NULL );
4776 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4777 g_object_set_data( holder, "set_action", act );
4778 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4779 // make sure it's disabled if we're not picking alpha
4780 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4781 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4782 }
4783 }
4787 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4788 {
4789 {
4790 /* Width */
4791 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4792 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4793 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4794 _("Pen Width"), _("Width:"),
4795 _("The width of the eraser pen (relative to the visible canvas area)"),
4796 "tools.eraser", "width", 15,
4797 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4798 1, 100, 1.0, 0.0,
4799 labels, values, G_N_ELEMENTS(labels),
4800 sp_ddc_width_value_changed, 0.01, 0, 100 );
4801 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4802 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4803 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4804 }
4806 {
4807 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4809 GtkTreeIter iter;
4810 gtk_list_store_append( model, &iter );
4811 gtk_list_store_set( model, &iter,
4812 0, _("Delete"),
4813 1, _("Delete objects touched by the eraser"),
4814 2, "delete_object",
4815 -1 );
4817 gtk_list_store_append( model, &iter );
4818 gtk_list_store_set( model, &iter,
4819 0, _("Cut"),
4820 1, _("Cut out from objects"),
4821 2, "difference",
4822 -1 );
4824 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4825 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4826 g_object_set_data( holder, "eraser_mode_action", act );
4828 ege_select_one_action_set_appearance( act, "full" );
4829 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4830 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4831 ege_select_one_action_set_icon_column( act, 2 );
4832 ege_select_one_action_set_tooltip_column( act, 1 );
4834 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4835 ege_select_one_action_set_active( act, eraserMode );
4836 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4837 }
4839 }
4841 //########################
4842 //## Text Toolbox ##
4843 //########################
4844 /*
4845 static void
4846 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4847 {
4848 //Call back for letter sizing spinbutton
4849 }
4851 static void
4852 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4853 {
4854 //Call back for line height spinbutton
4855 }
4857 static void
4858 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4859 {
4860 //Call back for horizontal kerning spinbutton
4861 }
4863 static void
4864 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4865 {
4866 //Call back for vertical kerning spinbutton
4867 }
4869 static void
4870 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4871 {
4872 //Call back for letter rotation spinbutton
4873 }*/
4875 namespace {
4877 bool popdown_visible = false;
4878 bool popdown_hasfocus = false;
4880 void
4881 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4882 {
4883 SPStyle *query =
4884 sp_style_new (SP_ACTIVE_DOCUMENT);
4886 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4888 int result_family =
4889 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4891 int result_style =
4892 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4894 int result_numbers =
4895 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4897 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4899 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4900 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
4901 // there are no texts in selection, read from prefs
4903 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4904 if (repr) {
4905 sp_style_read_from_repr (query, repr);
4906 if (g_object_get_data(tbl, "text_style_from_prefs")) {
4907 // do not reset the toolbar style from prefs if we already did it last time
4908 sp_style_unref(query);
4909 return;
4910 }
4911 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
4912 } else {
4913 sp_style_unref(query);
4914 return;
4915 }
4916 } else {
4917 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
4918 }
4920 if (query->text)
4921 {
4922 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4923 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4924 gtk_entry_set_text (GTK_ENTRY (entry), "");
4926 } else if (query->text->font_specification.value || query->text->font_family.value) {
4928 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4930 // Get the font that corresponds
4931 Glib::ustring familyName;
4933 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4934 if (font) {
4935 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4936 font->Unref();
4937 font = NULL;
4938 }
4940 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4942 Gtk::TreePath path;
4943 try {
4944 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4945 } catch (...) {
4946 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4947 sp_style_unref(query);
4948 return;
4949 }
4951 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4952 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4954 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4956 gtk_tree_selection_select_path (tselection, path.gobj());
4957 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4959 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4960 }
4962 //Size
4963 {
4964 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
4965 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
4966 g_object_set_data(tbl, "size-block", gpointer(1));
4967 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
4968 g_object_set_data(tbl, "size-block", gpointer(0));
4969 g_free(str);
4970 }
4972 //Anchor
4973 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4974 {
4975 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4976 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4977 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4978 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4979 }
4980 else
4981 {
4982 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4983 {
4984 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4985 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4986 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4987 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4988 }
4989 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4990 {
4991 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4992 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4993 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4994 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4995 }
4996 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4997 {
4998 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4999 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5000 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5001 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5002 }
5003 }
5005 //Style
5006 {
5007 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5009 gboolean active = gtk_toggle_button_get_active (button);
5010 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5012 if (active != check)
5013 {
5014 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5015 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5016 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5017 }
5018 }
5020 {
5021 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5023 gboolean active = gtk_toggle_button_get_active (button);
5024 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5026 if (active != check)
5027 {
5028 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5029 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5030 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5031 }
5032 }
5034 //Orientation
5035 //locking both buttons, changing one affect all group (both)
5036 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5037 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5039 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5040 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5042 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5043 {
5044 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5045 }
5046 else
5047 {
5048 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5049 }
5050 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5051 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5052 }
5054 sp_style_unref(query);
5055 }
5057 void
5058 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5059 {
5060 sp_text_toolbox_selection_changed (selection, tbl);
5061 }
5063 void
5064 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5065 {
5066 sp_text_toolbox_selection_changed (NULL, tbl);
5067 }
5069 void
5070 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5071 GObject *tbl)
5072 {
5073 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5074 GtkTreeModel *model = 0;
5075 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5076 GtkTreeIter iter;
5077 char *family = 0;
5079 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5080 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5082 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5083 return;
5084 }
5086 gtk_tree_model_get (model, &iter, 0, &family, -1);
5088 if (g_object_get_data (G_OBJECT (selection), "block"))
5089 {
5090 gtk_entry_set_text (GTK_ENTRY (entry), family);
5091 return;
5092 }
5094 gtk_entry_set_text (GTK_ENTRY (entry), family);
5096 SPStyle *query =
5097 sp_style_new (SP_ACTIVE_DOCUMENT);
5099 int result_fontspec =
5100 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5102 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5104 SPCSSAttr *css = sp_repr_css_attr_new ();
5107 // First try to get the font spec from the stored value
5108 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5110 if (fontSpec.empty()) {
5111 // Construct a new font specification if it does not yet exist
5112 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5113 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5114 fontFromStyle->Unref();
5115 }
5117 if (!fontSpec.empty()) {
5118 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5119 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5120 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5121 if (font) {
5122 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5124 // Set all the these just in case they were altered when finding the best
5125 // match for the new family and old style...
5127 gchar c[256];
5129 font->Family(c, 256);
5130 sp_repr_css_set_property (css, "font-family", c);
5132 font->Attribute( "weight", c, 256);
5133 sp_repr_css_set_property (css, "font-weight", c);
5135 font->Attribute("style", c, 256);
5136 sp_repr_css_set_property (css, "font-style", c);
5138 font->Attribute("stretch", c, 256);
5139 sp_repr_css_set_property (css, "font-stretch", c);
5141 font->Attribute("variant", c, 256);
5142 sp_repr_css_set_property (css, "font-variant", c);
5144 font->Unref();
5145 }
5146 }
5147 }
5149 // If querying returned nothing, set the default style of the tool (for new texts)
5150 if (result_fontspec == QUERY_STYLE_NOTHING)
5151 {
5152 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5153 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5154 }
5155 else
5156 {
5157 sp_desktop_set_style (desktop, css, true, true);
5158 }
5160 sp_style_unref(query);
5162 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5163 _("Text: Change font family"));
5164 sp_repr_css_attr_unref (css);
5165 g_free(family);
5166 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5168 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5169 }
5171 void
5172 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5173 GObject *tbl)
5174 {
5175 const char *family = gtk_entry_get_text (entry);
5177 try {
5178 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5179 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5180 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5181 gtk_tree_selection_select_path (selection, path.gobj());
5182 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5183 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5184 } catch (...) {
5185 if (family && strlen (family))
5186 {
5187 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5188 }
5189 }
5190 }
5192 void
5193 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5194 gpointer data)
5195 {
5196 if (g_object_get_data (G_OBJECT (button), "block")) return;
5197 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5198 int prop = GPOINTER_TO_INT(data);
5200 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5201 SPCSSAttr *css = sp_repr_css_attr_new ();
5203 switch (prop)
5204 {
5205 case 0:
5206 {
5207 sp_repr_css_set_property (css, "text-anchor", "start");
5208 sp_repr_css_set_property (css, "text-align", "start");
5209 break;
5210 }
5211 case 1:
5212 {
5213 sp_repr_css_set_property (css, "text-anchor", "middle");
5214 sp_repr_css_set_property (css, "text-align", "center");
5215 break;
5216 }
5218 case 2:
5219 {
5220 sp_repr_css_set_property (css, "text-anchor", "end");
5221 sp_repr_css_set_property (css, "text-align", "end");
5222 break;
5223 }
5225 case 3:
5226 {
5227 sp_repr_css_set_property (css, "text-anchor", "start");
5228 sp_repr_css_set_property (css, "text-align", "justify");
5229 break;
5230 }
5231 }
5233 SPStyle *query =
5234 sp_style_new (SP_ACTIVE_DOCUMENT);
5235 int result_numbers =
5236 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5238 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5239 if (result_numbers == QUERY_STYLE_NOTHING)
5240 {
5241 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5242 }
5244 sp_style_unref(query);
5246 sp_desktop_set_style (desktop, css, true, true);
5247 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5248 _("Text: Change alignment"));
5249 sp_repr_css_attr_unref (css);
5251 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5252 }
5254 void
5255 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5256 gpointer data)
5257 {
5258 if (g_object_get_data (G_OBJECT (button), "block")) return;
5260 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5261 SPCSSAttr *css = sp_repr_css_attr_new ();
5262 int prop = GPOINTER_TO_INT(data);
5263 bool active = gtk_toggle_button_get_active (button);
5265 SPStyle *query =
5266 sp_style_new (SP_ACTIVE_DOCUMENT);
5268 int result_fontspec =
5269 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5271 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5272 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5273 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5275 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5276 Glib::ustring newFontSpec = "";
5278 if (fontSpec.empty()) {
5279 // Construct a new font specification if it does not yet exist
5280 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5281 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5282 fontFromStyle->Unref();
5283 }
5285 switch (prop)
5286 {
5287 case 0:
5288 {
5289 if (!fontSpec.empty()) {
5290 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5291 }
5292 if (fontSpec != newFontSpec) {
5293 // Don't even set the bold if the font didn't exist on the system
5294 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5295 }
5296 break;
5297 }
5299 case 1:
5300 {
5301 if (!fontSpec.empty()) {
5302 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5303 }
5304 if (fontSpec != newFontSpec) {
5305 // Don't even set the italic if the font didn't exist on the system
5306 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5307 }
5308 break;
5309 }
5310 }
5312 if (!newFontSpec.empty()) {
5313 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5314 }
5316 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5317 if (result_fontspec == QUERY_STYLE_NOTHING)
5318 {
5319 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5320 }
5322 sp_style_unref(query);
5324 sp_desktop_set_style (desktop, css, true, true);
5325 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5326 _("Text: Change font style"));
5327 sp_repr_css_attr_unref (css);
5329 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5330 }
5332 void
5333 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5334 gpointer data)
5335 {
5336 if (g_object_get_data (G_OBJECT (button), "block")) {
5337 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5338 return;
5339 }
5341 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5342 SPCSSAttr *css = sp_repr_css_attr_new ();
5343 int prop = GPOINTER_TO_INT(data);
5345 switch (prop)
5346 {
5347 case 0:
5348 {
5349 sp_repr_css_set_property (css, "writing-mode", "lr");
5350 break;
5351 }
5353 case 1:
5354 {
5355 sp_repr_css_set_property (css, "writing-mode", "tb");
5356 break;
5357 }
5358 }
5360 SPStyle *query =
5361 sp_style_new (SP_ACTIVE_DOCUMENT);
5362 int result_numbers =
5363 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5365 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5366 if (result_numbers == QUERY_STYLE_NOTHING)
5367 {
5368 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5369 }
5371 sp_desktop_set_style (desktop, css, true, true);
5372 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5373 _("Text: Change orientation"));
5374 sp_repr_css_attr_unref (css);
5376 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5377 }
5379 gboolean
5380 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5381 {
5382 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5383 if (!desktop) return FALSE;
5385 switch (get_group0_keyval (event)) {
5386 case GDK_Escape: // defocus
5387 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5388 sp_text_toolbox_selection_changed (NULL, tbl); // update
5389 return TRUE; // I consumed the event
5390 break;
5391 }
5392 return FALSE;
5393 }
5395 gboolean
5396 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5397 {
5398 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5399 if (!desktop) return FALSE;
5401 switch (get_group0_keyval (event)) {
5402 case GDK_KP_Enter:
5403 case GDK_Return:
5404 case GDK_Escape: // defocus
5405 gtk_widget_hide (w);
5406 popdown_visible = false;
5407 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5408 return TRUE; // I consumed the event
5409 break;
5410 case GDK_w:
5411 case GDK_W:
5412 if (event->state & GDK_CONTROL_MASK) {
5413 gtk_widget_hide (w);
5414 popdown_visible = false;
5415 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5416 return TRUE; // I consumed the event
5417 }
5418 break;
5419 }
5420 return FALSE;
5421 }
5424 void
5425 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5426 GObject *tbl)
5427 {
5428 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5430 if (g_object_get_data (tbl, "size-block")) return;
5432 // If this is not from selecting a size in the list (in which case get_active will give the
5433 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5434 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5435 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5436 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5437 return;
5439 gdouble value = -1;
5440 {
5441 gchar *endptr;
5442 gchar *const text = gtk_combo_box_get_active_text(cbox);
5443 if (text) {
5444 value = g_strtod(text, &endptr);
5445 if (endptr == text) { // Conversion failed, non-numeric input.
5446 value = -1;
5447 }
5448 g_free(text);
5449 }
5450 }
5451 if (value <= 0) {
5452 return; // could not parse value
5453 }
5455 SPCSSAttr *css = sp_repr_css_attr_new ();
5456 Inkscape::CSSOStringStream osfs;
5457 osfs << value;
5458 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5460 SPStyle *query =
5461 sp_style_new (SP_ACTIVE_DOCUMENT);
5462 int result_numbers =
5463 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5465 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5466 if (result_numbers == QUERY_STYLE_NOTHING)
5467 {
5468 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5469 }
5471 sp_style_unref(query);
5473 sp_desktop_set_style (desktop, css, true, true);
5474 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5475 _("Text: Change font size"));
5476 sp_repr_css_attr_unref (css);
5478 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5479 }
5481 gboolean
5482 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5483 {
5484 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5485 if (!desktop) return FALSE;
5487 if (!g_object_get_data (tbl, "esc-pressed")) {
5488 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5489 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5490 sp_text_toolbox_size_changed (cbox, tbl);
5491 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5492 }
5493 return FALSE; // I consumed the event
5494 }
5497 gboolean
5498 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5499 {
5500 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5501 if (!desktop) return FALSE;
5503 switch (get_group0_keyval (event)) {
5504 case GDK_Escape: // defocus
5505 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5506 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5507 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5508 return TRUE; // I consumed the event
5509 break;
5510 case GDK_Return: // defocus
5511 case GDK_KP_Enter:
5512 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5513 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5514 sp_text_toolbox_size_changed (cbox, tbl);
5515 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5516 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5517 return TRUE; // I consumed the event
5518 break;
5519 }
5520 return FALSE;
5521 }
5523 void
5524 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5525 GObject *tbl)
5526 {
5527 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5528 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5529 int x, y;
5531 if (!popdown_visible)
5532 {
5533 gdk_window_get_origin (widget->window, &x, &y);
5534 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5535 gtk_widget_show_all (popdown);
5536 //sp_transientize (popdown);
5538 gdk_pointer_grab (widget->window, TRUE,
5539 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5540 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5541 GDK_POINTER_MOTION_MASK),
5542 NULL, NULL, GDK_CURRENT_TIME);
5544 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5546 popdown_visible = true;
5547 }
5548 else
5549 {
5550 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5551 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5552 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5553 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5554 gtk_widget_hide (popdown);
5555 popdown_visible = false;
5556 }
5557 }
5559 gboolean
5560 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5561 GdkEventFocus */*event*/,
5562 GObject */*tbl*/)
5563 {
5564 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5565 return FALSE;
5566 }
5568 gboolean
5569 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5570 GdkEventFocus */*event*/,
5571 GObject */*tbl*/)
5572 {
5573 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5575 if (popdown_hasfocus) {
5576 gtk_widget_hide (popdown);
5577 popdown_hasfocus = false;
5578 popdown_visible = false;
5579 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5580 return TRUE;
5581 }
5582 return FALSE;
5583 }
5585 gboolean
5586 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5587 GdkEventFocus */*event*/,
5588 GObject */*tbl*/)
5589 {
5590 popdown_hasfocus = true;
5591 return TRUE;
5592 }
5595 void
5596 cell_data_func (GtkTreeViewColumn */*column*/,
5597 GtkCellRenderer *cell,
5598 GtkTreeModel *tree_model,
5599 GtkTreeIter *iter,
5600 gpointer /*data*/)
5601 {
5602 gchar *family;
5603 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5604 gchar *const family_escaped = g_markup_escape_text(family, -1);
5606 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5607 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5609 std::stringstream markup;
5610 markup << family_escaped << " <span foreground='darkgray' font_family='"
5611 << family_escaped << "'>" << sample_escaped << "</span>";
5612 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5614 g_free(family);
5615 g_free(family_escaped);
5616 g_free(sample_escaped);
5617 }
5619 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5620 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5621 if (completion) {
5622 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5623 g_object_unref (completion);
5624 }
5625 }
5627 GtkWidget*
5628 sp_text_toolbox_new (SPDesktop *desktop)
5629 {
5630 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5631 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5633 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5634 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5636 GtkTooltips *tt = gtk_tooltips_new();
5637 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5639 ////////////Family
5640 //Window
5641 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5642 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5644 //Entry
5645 GtkWidget *entry = gtk_entry_new ();
5646 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5647 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5648 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5649 gtk_entry_completion_set_text_column (completion, 0);
5650 gtk_entry_completion_set_minimum_key_length (completion, 1);
5651 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5652 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5653 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5654 gtk_toolbar_append_widget( tbl, entry, "", "" );
5655 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5657 //Button
5658 GtkWidget *button = gtk_button_new ();
5659 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5660 gtk_toolbar_append_widget( tbl, button, "", "");
5662 //Popdown
5663 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5664 GtkWidget *treeview = gtk_tree_view_new ();
5666 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5667 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5668 gtk_tree_view_column_pack_start (column, cell, FALSE);
5669 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5670 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5671 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5673 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5674 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5675 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5677 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5679 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5680 gtk_container_add (GTK_CONTAINER (sw), treeview);
5682 gtk_container_add (GTK_CONTAINER (window), sw);
5683 gtk_widget_set_size_request (window, 300, 450);
5685 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5686 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5687 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5689 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5691 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5692 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5693 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5695 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5696 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5698 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5699 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5700 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5701 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5702 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5704 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5705 GtkWidget *box = gtk_event_box_new ();
5706 gtk_container_add (GTK_CONTAINER (box), image);
5707 gtk_toolbar_append_widget( tbl, box, "", "");
5708 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5709 GtkTooltips *tooltips = gtk_tooltips_new ();
5710 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5711 gtk_widget_hide (GTK_WIDGET (box));
5712 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5714 ////////////Size
5715 gchar const *const sizes[] = {
5716 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5717 "16", "18", "20", "22", "24", "28",
5718 "32", "36", "40", "48", "56", "64", "72", "144"
5719 };
5721 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5722 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5723 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5724 }
5725 gtk_widget_set_size_request (cbox, 80, -1);
5726 gtk_toolbar_append_widget( tbl, cbox, "", "");
5727 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5728 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5729 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5730 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5732 ////////////Text anchor
5733 GtkWidget *group = gtk_radio_button_new (NULL);
5734 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5735 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5737 // left
5738 GtkWidget *rbutton = 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_LEFT, 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-start", rbutton);
5745 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5746 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5748 // center
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_CENTER, 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-middle", rbutton);
5756 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5757 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5759 // right
5760 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5761 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5762 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5763 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5765 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5766 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5767 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5768 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5770 // fill
5771 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5772 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5773 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5774 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5776 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5777 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5778 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5779 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5781 gtk_toolbar_append_widget( tbl, row, "", "");
5783 //spacer
5784 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5786 ////////////Text style
5787 row = gtk_hbox_new (FALSE, 4);
5789 // bold
5790 rbutton = gtk_toggle_button_new ();
5791 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5792 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5793 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5794 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5796 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5797 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5798 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5800 // italic
5801 rbutton = gtk_toggle_button_new ();
5802 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5803 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5804 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5805 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5807 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5808 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5809 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5811 gtk_toolbar_append_widget( tbl, row, "", "");
5813 //spacer
5814 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5816 ////////////Text orientation
5817 group = gtk_radio_button_new (NULL);
5818 row = gtk_hbox_new (FALSE, 4);
5819 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5821 // horizontal
5822 rbutton = group;
5823 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5824 gtk_container_add (GTK_CONTAINER (rbutton),
5825 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5826 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5827 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5829 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5830 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5831 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5833 // vertical
5834 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5835 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5836 gtk_container_add (GTK_CONTAINER (rbutton),
5837 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5838 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5839 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5841 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5842 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5843 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5844 gtk_toolbar_append_widget( tbl, row, "", "" );
5847 //watch selection
5848 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5850 sigc::connection *c_selection_changed =
5851 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5852 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5853 pool->add_connection ("selection-changed", c_selection_changed);
5855 sigc::connection *c_selection_modified =
5856 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5857 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5858 pool->add_connection ("selection-modified", c_selection_modified);
5860 sigc::connection *c_subselection_changed =
5861 new sigc::connection (desktop->connectToolSubselectionChanged
5862 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5863 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5865 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5868 gtk_widget_show_all( GTK_WIDGET(tbl) );
5870 return GTK_WIDGET(tbl);
5871 } // end of sp_text_toolbox_new()
5873 }//<unnamed> namespace
5876 //#########################
5877 //## Connector ##
5878 //#########################
5880 static void sp_connector_path_set_avoid(void)
5881 {
5882 cc_selection_set_avoid(true);
5883 }
5886 static void sp_connector_path_set_ignore(void)
5887 {
5888 cc_selection_set_avoid(false);
5889 }
5893 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5894 {
5895 // quit if run by the _changed callbacks
5896 if (g_object_get_data( tbl, "freeze" )) {
5897 return;
5898 }
5900 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5901 SPDocument *doc = sp_desktop_document(desktop);
5903 if (!sp_document_get_undo_sensitive(doc))
5904 {
5905 return;
5906 }
5908 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5910 if ( repr->attribute("inkscape:connector-spacing") ) {
5911 gdouble priorValue = gtk_adjustment_get_value(adj);
5912 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5913 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5914 return;
5915 }
5916 } else if ( adj->value == defaultConnSpacing ) {
5917 return;
5918 }
5920 // in turn, prevent callbacks from responding
5921 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5923 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5924 SP_OBJECT(desktop->namedview)->updateRepr();
5926 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5927 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5928 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5929 NR::Matrix m = NR::identity();
5930 avoid_item_move(&m, item);
5931 }
5933 if (items) {
5934 g_slist_free(items);
5935 }
5937 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5938 _("Change connector spacing"));
5940 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5942 spinbutton_defocus(GTK_OBJECT(tbl));
5943 }
5945 static void sp_connector_graph_layout(void)
5946 {
5947 if (!SP_ACTIVE_DESKTOP) return;
5949 // hack for clones, see comment in align-and-distribute.cpp
5950 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5951 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5953 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5955 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5957 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5958 }
5960 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5961 {
5962 if ( gtk_toggle_action_get_active( act ) ) {
5963 prefs_set_string_attribute("tools.connector", "directedlayout",
5964 "true");
5965 } else {
5966 prefs_set_string_attribute("tools.connector", "directedlayout",
5967 "false");
5968 }
5969 }
5971 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5972 {
5973 if ( gtk_toggle_action_get_active( act ) ) {
5974 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5975 "true");
5976 } else {
5977 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5978 "false");
5979 }
5980 }
5983 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5984 {
5985 prefs_set_double_attribute("tools.connector", "length", adj->value);
5986 spinbutton_defocus(GTK_OBJECT(tbl));
5987 }
5989 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5990 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5991 bool /*is_interactive*/, gpointer data)
5992 {
5993 GtkWidget *tbl = GTK_WIDGET(data);
5995 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5996 return;
5997 }
5998 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5999 return;
6000 }
6002 GtkAdjustment *adj = (GtkAdjustment*)
6003 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6004 gdouble spacing = defaultConnSpacing;
6005 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6007 gtk_adjustment_set_value(adj, spacing);
6008 }
6011 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6012 NULL, /* child_added */
6013 NULL, /* child_removed */
6014 connector_tb_event_attr_changed,
6015 NULL, /* content_changed */
6016 NULL /* order_changed */
6017 };
6020 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6021 {
6022 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6024 {
6025 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6026 _("Avoid"),
6027 _("Make connectors avoid selected objects"),
6028 "connector_avoid",
6029 secondarySize );
6030 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6031 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6032 }
6034 {
6035 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6036 _("Ignore"),
6037 _("Make connectors ignore selected objects"),
6038 "connector_ignore",
6039 secondarySize );
6040 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6041 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6042 }
6044 EgeAdjustmentAction* eact = 0;
6046 // Spacing spinbox
6047 eact = create_adjustment_action( "ConnectorSpacingAction",
6048 _("Connector Spacing"), _("Spacing:"),
6049 _("The amount of space left around objects by auto-routing connectors"),
6050 "tools.connector", "spacing", defaultConnSpacing,
6051 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6052 0, 100, 1.0, 10.0,
6053 0, 0, 0,
6054 connector_spacing_changed, 1, 0 );
6055 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6057 // Graph (connector network) layout
6058 {
6059 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6060 _("Graph"),
6061 _("Nicely arrange selected connector network"),
6062 "graph_layout",
6063 secondarySize );
6064 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6065 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6066 }
6068 // Default connector length spinbox
6069 eact = create_adjustment_action( "ConnectorLengthAction",
6070 _("Connector Length"), _("Length:"),
6071 _("Ideal length for connectors when layout is applied"),
6072 "tools.connector", "length", 100,
6073 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6074 10, 1000, 10.0, 100.0,
6075 0, 0, 0,
6076 connector_length_changed, 1, 0 );
6077 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6080 // Directed edges toggle button
6081 {
6082 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6083 _("Downwards"),
6084 _("Make connectors with end-markers (arrows) point downwards"),
6085 "directed_graph",
6086 Inkscape::ICON_SIZE_DECORATION );
6087 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6089 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6090 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6091 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6093 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6094 }
6096 // Avoid overlaps toggle button
6097 {
6098 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6099 _("Remove overlaps"),
6100 _("Do not allow overlapping shapes"),
6101 "remove_overlaps",
6102 Inkscape::ICON_SIZE_DECORATION );
6103 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6105 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6106 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6107 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6109 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6110 }
6112 // Code to watch for changes to the connector-spacing attribute in
6113 // the XML.
6114 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6115 g_assert(repr != NULL);
6117 purge_repr_listener( holder, holder );
6119 if (repr) {
6120 g_object_set_data( holder, "repr", repr );
6121 Inkscape::GC::anchor(repr);
6122 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6123 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6124 }
6125 } // end of sp_connector_toolbox_prep()
6128 //#########################
6129 //## Paintbucket ##
6130 //#########################
6132 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6133 {
6134 gint channels = ege_select_one_action_get_active( act );
6135 flood_channels_set_channels( channels );
6136 }
6138 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6139 {
6140 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6141 }
6143 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6144 {
6145 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6146 }
6148 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6149 {
6150 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6151 SPUnit const *unit = tracker->getActiveUnit();
6153 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6155 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6156 }
6158 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6159 {
6160 // FIXME: make defaults settable via Inkscape Options
6161 struct KeyValue {
6162 char const *key;
6163 double value;
6164 } const key_values[] = {
6165 {"threshold", 15},
6166 {"offset", 0.0}
6167 };
6169 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6170 KeyValue const &kv = key_values[i];
6171 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6172 if ( adj ) {
6173 gtk_adjustment_set_value(adj, kv.value);
6174 }
6175 }
6177 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6178 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6179 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6180 ege_select_one_action_set_active( autogap_action, 0 );
6181 }
6183 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6184 {
6185 EgeAdjustmentAction* eact = 0;
6187 {
6188 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6190 GList* items = 0;
6191 gint count = 0;
6192 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6193 {
6194 GtkTreeIter iter;
6195 gtk_list_store_append( model, &iter );
6196 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6197 count++;
6198 }
6199 g_list_free( items );
6200 items = 0;
6201 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6202 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6203 ege_select_one_action_set_appearance( act1, "compact" );
6204 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6205 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6206 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6207 g_object_set_data( holder, "channels_action", act1 );
6208 }
6210 // Spacing spinbox
6211 {
6212 eact = create_adjustment_action(
6213 "ThresholdAction",
6214 _("Fill Threshold"), _("Threshold:"),
6215 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6216 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6217 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6218 0, 0, 0,
6219 paintbucket_threshold_changed, 1, 0 );
6221 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6222 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6223 }
6225 // Create the units menu.
6226 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6227 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6228 if (stored_unit)
6229 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6230 g_object_set_data( holder, "tracker", tracker );
6231 {
6232 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6233 gtk_action_group_add_action( mainActions, act );
6234 }
6236 // Offset spinbox
6237 {
6238 eact = create_adjustment_action(
6239 "OffsetAction",
6240 _("Grow/shrink by"), _("Grow/shrink by:"),
6241 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6242 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6243 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6244 0, 0, 0,
6245 paintbucket_offset_changed, 1, 2);
6246 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6248 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6249 }
6251 /* Auto Gap */
6252 {
6253 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6255 GList* items = 0;
6256 gint count = 0;
6257 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6258 {
6259 GtkTreeIter iter;
6260 gtk_list_store_append( model, &iter );
6261 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6262 count++;
6263 }
6264 g_list_free( items );
6265 items = 0;
6266 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6267 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6268 ege_select_one_action_set_appearance( act2, "compact" );
6269 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6270 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6271 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6272 g_object_set_data( holder, "autogap_action", act2 );
6273 }
6275 /* Reset */
6276 {
6277 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6278 _("Defaults"),
6279 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6280 GTK_STOCK_CLEAR );
6281 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6282 gtk_action_group_add_action( mainActions, act );
6283 gtk_action_set_sensitive( act, TRUE );
6284 }
6286 }
6288 /*
6289 Local Variables:
6290 mode:c++
6291 c-file-style:"stroustrup"
6292 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6293 indent-tabs-mode:nil
6294 fill-column:99
6295 End:
6296 */
6297 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :