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