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 <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
57 #include "live_effects/effect.h"
59 #include "inkscape.h"
60 #include "conn-avoid-ref.h"
63 #include "select-toolbar.h"
64 #include "gradient-toolbar.h"
66 #include "connector-context.h"
67 #include "node-context.h"
68 #include "draw-context.h"
69 #include "shape-editor.h"
70 #include "tweak-context.h"
71 #include "sp-rect.h"
72 #include "box3d.h"
73 #include "box3d-context.h"
74 #include "sp-star.h"
75 #include "sp-spiral.h"
76 #include "sp-ellipse.h"
77 #include "sp-text.h"
78 #include "sp-flowtext.h"
79 #include "sp-clippath.h"
80 #include "sp-mask.h"
81 #include "style.h"
82 #include "selection.h"
83 #include "selection-chemistry.h"
84 #include "document-private.h"
85 #include "desktop-style.h"
86 #include "../libnrtype/font-lister.h"
87 #include "../libnrtype/font-instance.h"
88 #include "../connection-pool.h"
89 #include "../prefs-utils.h"
90 #include "../inkscape-stock.h"
91 #include "icon.h"
92 #include "graphlayout/graphlayout.h"
93 #include "interface.h"
94 #include "shortcuts.h"
96 #include "mod360.h"
98 #include "toolbox.h"
100 #include "flood-context.h"
102 #include "ink-action.h"
103 #include "ege-adjustment-action.h"
104 #include "ege-output-action.h"
105 #include "ege-select-one-action.h"
106 #include "helper/unit-tracker.h"
108 #include "svg/css-ostringstream.h"
110 #include "widgets/calligraphic-profile-rename.h"
112 using Inkscape::UnitTracker;
114 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
115 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
117 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
130 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
137 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
138 static Inkscape::IconSize sizeChoices[] = {
139 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
140 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
141 Inkscape::ICON_SIZE_MENU
142 };
143 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
144 return sizeChoices[index];
145 }
147 static struct {
148 gchar const *type_name;
149 gchar const *data_name;
150 sp_verb_t verb;
151 sp_verb_t doubleclick_verb;
152 } const tools[] = {
153 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
154 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
155 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
156 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
157 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
158 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
159 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
160 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
161 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
162 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
163 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
164 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
165 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
166 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
167 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
168 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
169 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
170 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
171 { NULL, NULL, 0, 0 }
172 };
174 static struct {
175 gchar const *type_name;
176 gchar const *data_name;
177 GtkWidget *(*create_func)(SPDesktop *desktop);
178 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
179 gchar const *ui_name;
180 gint swatch_verb_id;
181 gchar const *swatch_tool;
182 gchar const *swatch_tip;
183 } const aux_toolboxes[] = {
184 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
185 SP_VERB_INVALID, 0, 0},
186 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
187 SP_VERB_INVALID, 0, 0},
188 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
189 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
190 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
191 SP_VERB_INVALID, 0, 0},
192 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
193 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
194 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
195 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
196 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
197 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
198 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
199 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
200 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
201 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
202 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
203 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
204 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
205 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
206 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
207 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
208 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
209 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
210 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
211 SP_VERB_INVALID, 0, 0},
212 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
213 SP_VERB_INVALID, 0, 0},
214 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
215 SP_VERB_INVALID, 0, 0},
216 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
217 SP_VERB_INVALID, 0, 0},
218 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
219 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
220 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
221 };
223 #define TOOLBAR_SLIDER_HINT "full"
225 static gchar const * ui_descr =
226 "<ui>"
227 " <toolbar name='SelectToolbar'>"
228 " <toolitem action='EditSelectAll' />"
229 " <toolitem action='EditSelectAllInAllLayers' />"
230 " <toolitem action='EditDeselect' />"
231 " <separator />"
232 " <toolitem action='ObjectRotate90CCW' />"
233 " <toolitem action='ObjectRotate90' />"
234 " <toolitem action='ObjectFlipHorizontally' />"
235 " <toolitem action='ObjectFlipVertically' />"
236 " <separator />"
237 " <toolitem action='SelectionToBack' />"
238 " <toolitem action='SelectionLower' />"
239 " <toolitem action='SelectionRaise' />"
240 " <toolitem action='SelectionToFront' />"
241 " <separator />"
242 " <toolitem action='XAction' />"
243 " <toolitem action='YAction' />"
244 " <toolitem action='WidthAction' />"
245 " <toolitem action='LockAction' />"
246 " <toolitem action='HeightAction' />"
247 " <toolitem action='UnitsAction' />"
248 " <separator />"
249 " <toolitem action='transform_affect_label' />"
250 " <toolitem action='transform_stroke' />"
251 " <toolitem action='transform_corners' />"
252 " <toolitem action='transform_gradient' />"
253 " <toolitem action='transform_pattern' />"
254 " </toolbar>"
256 " <toolbar name='NodeToolbar'>"
257 " <toolitem action='NodeInsertAction' />"
258 " <toolitem action='NodeDeleteAction' />"
259 " <separator />"
260 " <toolitem action='NodeJoinAction' />"
261 " <toolitem action='NodeBreakAction' />"
262 " <separator />"
263 " <toolitem action='NodeJoinSegmentAction' />"
264 " <toolitem action='NodeDeleteSegmentAction' />"
265 " <separator />"
266 " <toolitem action='NodeCuspAction' />"
267 " <toolitem action='NodeSmoothAction' />"
268 " <toolitem action='NodeSymmetricAction' />"
269 " <separator />"
270 " <toolitem action='NodeLineAction' />"
271 " <toolitem action='NodeCurveAction' />"
272 " <separator />"
273 " <toolitem action='ObjectToPath' />"
274 " <toolitem action='StrokeToPath' />"
275 " <separator />"
276 " <toolitem action='NodeXAction' />"
277 " <toolitem action='NodeYAction' />"
278 " <toolitem action='NodeUnitsAction' />"
279 " <separator />"
280 " <toolitem action='ObjectEditClipPathAction' />"
281 " <toolitem action='ObjectEditMaskPathAction' />"
282 " <toolitem action='EditNextLPEParameterAction' />"
283 " <separator />"
284 " <toolitem action='NodesShowHandlesAction' />"
285 " <toolitem action='NodesShowHelperpath' />"
286 " </toolbar>"
288 " <toolbar name='TweakToolbar'>"
289 " <toolitem action='TweakWidthAction' />"
290 " <separator />"
291 " <toolitem action='TweakForceAction' />"
292 " <toolitem action='TweakPressureAction' />"
293 " <separator />"
294 " <toolitem action='TweakModeAction' />"
295 " <separator />"
296 " <toolitem action='TweakFidelityAction' />"
297 " <separator />"
298 " <toolitem action='TweakChannelsLabel' />"
299 " <toolitem action='TweakDoH' />"
300 " <toolitem action='TweakDoS' />"
301 " <toolitem action='TweakDoL' />"
302 " <toolitem action='TweakDoO' />"
303 " </toolbar>"
305 " <toolbar name='ZoomToolbar'>"
306 " <toolitem action='ZoomIn' />"
307 " <toolitem action='ZoomOut' />"
308 " <separator />"
309 " <toolitem action='Zoom1:0' />"
310 " <toolitem action='Zoom1:2' />"
311 " <toolitem action='Zoom2:1' />"
312 " <separator />"
313 " <toolitem action='ZoomSelection' />"
314 " <toolitem action='ZoomDrawing' />"
315 " <toolitem action='ZoomPage' />"
316 " <toolitem action='ZoomPageWidth' />"
317 " <separator />"
318 " <toolitem action='ZoomPrev' />"
319 " <toolitem action='ZoomNext' />"
320 " </toolbar>"
322 " <toolbar name='StarToolbar'>"
323 " <separator />"
324 " <toolitem action='StarStateAction' />"
325 " <separator />"
326 " <toolitem action='FlatAction' />"
327 " <separator />"
328 " <toolitem action='MagnitudeAction' />"
329 " <toolitem action='SpokeAction' />"
330 " <toolitem action='RoundednessAction' />"
331 " <toolitem action='RandomizationAction' />"
332 " <separator />"
333 " <toolitem action='StarResetAction' />"
334 " </toolbar>"
336 " <toolbar name='RectToolbar'>"
337 " <toolitem action='RectStateAction' />"
338 " <toolitem action='RectWidthAction' />"
339 " <toolitem action='RectHeightAction' />"
340 " <toolitem action='RadiusXAction' />"
341 " <toolitem action='RadiusYAction' />"
342 " <toolitem action='RectUnitsAction' />"
343 " <separator />"
344 " <toolitem action='RectResetAction' />"
345 " </toolbar>"
347 " <toolbar name='3DBoxToolbar'>"
348 " <toolitem action='3DBoxAngleXAction' />"
349 " <toolitem action='3DBoxVPXStateAction' />"
350 " <separator />"
351 " <toolitem action='3DBoxAngleYAction' />"
352 " <toolitem action='3DBoxVPYStateAction' />"
353 " <separator />"
354 " <toolitem action='3DBoxAngleZAction' />"
355 " <toolitem action='3DBoxVPZStateAction' />"
356 " </toolbar>"
358 " <toolbar name='SpiralToolbar'>"
359 " <toolitem action='SpiralStateAction' />"
360 " <toolitem action='SpiralRevolutionAction' />"
361 " <toolitem action='SpiralExpansionAction' />"
362 " <toolitem action='SpiralT0Action' />"
363 " <separator />"
364 " <toolitem action='SpiralResetAction' />"
365 " </toolbar>"
367 " <toolbar name='PenToolbar'>"
368 " <toolitem action='FreehandModeActionPenTemp' />"
369 " <toolitem action='FreehandModeActionPen' />"
370 " <separator />"
371 " <toolitem action='SetPenShapeAction'/>"
372 " </toolbar>"
374 " <toolbar name='PencilToolbar'>"
375 " <toolitem action='FreehandModeActionPencilTemp' />"
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 " <toolitem action='SaveDeleteProfileAction'/>"
389 " <separator />"
390 " <toolitem action='CalligraphyWidthAction' />"
391 " <toolitem action='PressureAction' />"
392 " <toolitem action='TraceAction' />"
393 " <toolitem action='ThinningAction' />"
394 " <separator />"
395 " <toolitem action='AngleAction' />"
396 " <toolitem action='TiltAction' />"
397 " <toolitem action='FixationAction' />"
398 " <separator />"
399 " <toolitem action='CapRoundingAction' />"
400 " <separator />"
401 " <toolitem action='TremorAction' />"
402 " <toolitem action='WiggleAction' />"
403 " <toolitem action='MassAction' />"
404 " <separator />"
405 " </toolbar>"
407 " <toolbar name='ArcToolbar'>"
408 " <toolitem action='ArcStateAction' />"
409 " <separator />"
410 " <toolitem action='ArcStartAction' />"
411 " <toolitem action='ArcEndAction' />"
412 " <separator />"
413 " <toolitem action='ArcOpenAction' />"
414 " <separator />"
415 " <toolitem action='ArcResetAction' />"
416 " <separator />"
417 " </toolbar>"
419 " <toolbar name='PaintbucketToolbar'>"
420 " <toolitem action='ChannelsAction' />"
421 " <separator />"
422 " <toolitem action='ThresholdAction' />"
423 " <separator />"
424 " <toolitem action='OffsetAction' />"
425 " <toolitem action='PaintbucketUnitsAction' />"
426 " <separator />"
427 " <toolitem action='AutoGapAction' />"
428 " <separator />"
429 " <toolitem action='PaintbucketResetAction' />"
430 " </toolbar>"
432 " <toolbar name='EraserToolbar'>"
433 " <toolitem action='EraserWidthAction' />"
434 " <separator />"
435 " <toolitem action='EraserModeAction' />"
436 " </toolbar>"
438 " <toolbar name='DropperToolbar'>"
439 " <toolitem action='DropperOpacityAction' />"
440 " <toolitem action='DropperPickAlphaAction' />"
441 " <toolitem action='DropperSetAlphaAction' />"
442 " </toolbar>"
444 " <toolbar name='ConnectorToolbar'>"
445 " <toolitem action='ConnectorAvoidAction' />"
446 " <toolitem action='ConnectorIgnoreAction' />"
447 " <toolitem action='ConnectorSpacingAction' />"
448 " <toolitem action='ConnectorGraphAction' />"
449 " <toolitem action='ConnectorLengthAction' />"
450 " <toolitem action='ConnectorDirectedAction' />"
451 " <toolitem action='ConnectorOverlapAction' />"
452 " </toolbar>"
454 "</ui>"
455 ;
457 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
459 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
461 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
462 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
464 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
465 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
467 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
468 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
471 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
472 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
473 Inkscape::UI::View::View *view, GtkTooltips *tt);
475 class VerbAction : public Gtk::Action {
476 public:
477 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
479 virtual ~VerbAction();
480 virtual void set_active(bool active = true);
482 protected:
483 virtual Gtk::Widget* create_menu_item_vfunc();
484 virtual Gtk::Widget* create_tool_item_vfunc();
486 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
487 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
489 virtual void on_activate();
491 private:
492 Inkscape::Verb* verb;
493 Inkscape::Verb* verb2;
494 Inkscape::UI::View::View *view;
495 GtkTooltips *tooltips;
496 bool active;
498 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
499 };
502 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
503 {
504 Glib::RefPtr<VerbAction> result;
505 SPAction *action = verb->get_action(view);
506 if ( action ) {
507 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
508 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
509 }
511 return result;
512 }
514 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
515 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
516 verb(verb),
517 verb2(verb2),
518 view(view),
519 tooltips(tooltips),
520 active(false)
521 {
522 }
524 VerbAction::~VerbAction()
525 {
526 }
528 Gtk::Widget* VerbAction::create_menu_item_vfunc()
529 {
530 // First call in to get the icon rendered if present in SVG
531 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
532 delete widget;
533 widget = 0;
535 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
536 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
537 return widg;
538 }
540 Gtk::Widget* VerbAction::create_tool_item_vfunc()
541 {
542 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
543 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
544 GtkWidget* toolbox = 0;
545 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
546 SP_BUTTON_TYPE_TOGGLE,
547 verb,
548 verb2,
549 view,
550 tooltips );
551 if ( active ) {
552 sp_button_toggle_set_down( SP_BUTTON(button), active);
553 }
554 gtk_widget_show_all( button );
555 Gtk::Widget* wrapped = Glib::wrap(button);
556 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
557 holder->add(*wrapped);
559 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
560 return holder;
561 }
563 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
564 {
565 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
566 Gtk::Action::connect_proxy_vfunc(proxy);
567 }
569 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
570 {
571 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
572 Gtk::Action::disconnect_proxy_vfunc(proxy);
573 }
575 void VerbAction::set_active(bool active)
576 {
577 this->active = active;
578 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
579 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
580 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
581 if (ti) {
582 // *should* have one child that is the SPButton
583 Gtk::Widget* child = ti->get_child();
584 if ( child && SP_IS_BUTTON(child->gobj()) ) {
585 SPButton* button = SP_BUTTON(child->gobj());
586 sp_button_toggle_set_down( button, active );
587 }
588 }
589 }
590 }
592 void VerbAction::on_activate()
593 {
594 if ( verb ) {
595 SPAction *action = verb->get_action(view);
596 if ( action ) {
597 sp_action_perform(action, 0);
598 }
599 }
600 }
602 /* Global text entry widgets necessary for update */
603 /* GtkWidget *dropper_rgb_entry,
604 *dropper_opacity_entry ; */
605 // should be made a private member once this is converted to class
607 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
608 connection->disconnect();
609 delete connection;
610 }
612 static void purge_repr_listener( GObject* obj, GObject* tbl )
613 {
614 (void)obj;
615 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
616 if (oldrepr) { // remove old listener
617 sp_repr_remove_listener_by_data(oldrepr, tbl);
618 Inkscape::GC::release(oldrepr);
619 oldrepr = 0;
620 g_object_set_data( tbl, "repr", NULL );
621 }
622 }
624 GtkWidget *
625 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
626 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
627 Inkscape::UI::View::View *view, GtkTooltips *tt)
628 {
629 SPAction *action = verb->get_action(view);
630 if (!action) return NULL;
632 SPAction *doubleclick_action;
633 if (doubleclick_verb)
634 doubleclick_action = doubleclick_verb->get_action(view);
635 else
636 doubleclick_action = NULL;
638 /* fixme: Handle sensitive/unsensitive */
639 /* fixme: Implement sp_button_new_from_action */
640 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
641 gtk_widget_show(b);
644 unsigned int shortcut = sp_shortcut_get_primary(verb);
645 if (shortcut) {
646 gchar key[256];
647 sp_ui_shortcut_string(shortcut, key);
648 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
649 if ( t ) {
650 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
651 }
652 g_free(tip);
653 } else {
654 if ( t ) {
655 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
656 }
657 }
659 return b;
660 }
663 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
664 {
665 SPAction* targetAction = SP_ACTION(user_data);
666 if ( targetAction ) {
667 sp_action_perform( targetAction, NULL );
668 }
669 }
671 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
672 {
673 if ( data ) {
674 GtkAction* act = GTK_ACTION(data);
675 gtk_action_set_sensitive( act, sensitive );
676 }
677 }
679 static SPActionEventVector action_event_vector = {
680 {NULL},
681 NULL,
682 NULL,
683 sp_action_action_set_sensitive,
684 NULL,
685 NULL
686 };
688 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
689 {
690 GtkAction* act = 0;
692 SPAction* targetAction = verb->get_action(view);
693 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
694 act = GTK_ACTION(inky);
695 gtk_action_set_sensitive( act, targetAction->sensitive );
697 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
699 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
700 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
702 return act;
703 }
705 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
706 {
707 Inkscape::UI::View::View *view = desktop;
708 gint verbsToUse[] = {
709 // disabled until we have icons for them:
710 //find
711 //SP_VERB_EDIT_TILE,
712 //SP_VERB_EDIT_UNTILE,
713 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
714 SP_VERB_DIALOG_DISPLAY,
715 SP_VERB_DIALOG_FILL_STROKE,
716 SP_VERB_DIALOG_NAMEDVIEW,
717 SP_VERB_DIALOG_TEXT,
718 SP_VERB_DIALOG_XML_EDITOR,
719 SP_VERB_EDIT_CLONE,
720 SP_VERB_EDIT_COPY,
721 SP_VERB_EDIT_CUT,
722 SP_VERB_EDIT_DUPLICATE,
723 SP_VERB_EDIT_PASTE,
724 SP_VERB_EDIT_REDO,
725 SP_VERB_EDIT_UNDO,
726 SP_VERB_EDIT_UNLINK_CLONE,
727 SP_VERB_FILE_EXPORT,
728 SP_VERB_FILE_IMPORT,
729 SP_VERB_FILE_NEW,
730 SP_VERB_FILE_OPEN,
731 SP_VERB_FILE_PRINT,
732 SP_VERB_FILE_SAVE,
733 SP_VERB_OBJECT_TO_CURVE,
734 SP_VERB_SELECTION_GROUP,
735 SP_VERB_SELECTION_OUTLINE,
736 SP_VERB_SELECTION_UNGROUP,
737 SP_VERB_ZOOM_1_1,
738 SP_VERB_ZOOM_1_2,
739 SP_VERB_ZOOM_2_1,
740 SP_VERB_ZOOM_DRAWING,
741 SP_VERB_ZOOM_IN,
742 SP_VERB_ZOOM_NEXT,
743 SP_VERB_ZOOM_OUT,
744 SP_VERB_ZOOM_PAGE,
745 SP_VERB_ZOOM_PAGE_WIDTH,
746 SP_VERB_ZOOM_PREV,
747 SP_VERB_ZOOM_SELECTION,
748 };
750 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
752 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
753 Glib::RefPtr<Gtk::ActionGroup> mainActions;
754 if ( groups.find(desktop) != groups.end() ) {
755 mainActions = groups[desktop];
756 }
758 if ( !mainActions ) {
759 mainActions = Gtk::ActionGroup::create("main");
760 groups[desktop] = mainActions;
761 }
763 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
764 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
765 if ( verb ) {
766 if (!mainActions->get_action(verb->get_id())) {
767 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
768 mainActions->add(Glib::wrap(act));
769 }
770 }
771 }
773 if ( !mainActions->get_action("ToolZoom") ) {
774 GtkTooltips *tt = gtk_tooltips_new();
775 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
776 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
777 if ( va ) {
778 mainActions->add(va);
779 if ( i == 0 ) {
780 va->set_active(true);
781 }
782 }
783 }
784 }
787 return mainActions;
788 }
791 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
792 {
793 gtk_widget_set_size_request( widget,
794 widget->allocation.width,
795 widget->allocation.height );
796 }
798 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
799 {
800 gtk_widget_set_size_request( widget, -1, -1 );
801 }
805 GtkWidget *
806 sp_tool_toolbox_new()
807 {
808 GtkTooltips *tt = gtk_tooltips_new();
809 GtkWidget* tb = gtk_toolbar_new();
810 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
811 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
813 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
814 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
816 gtk_widget_set_sensitive(tb, FALSE);
818 GtkWidget *hb = gtk_handle_box_new();
819 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
820 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
821 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
823 gtk_container_add(GTK_CONTAINER(hb), tb);
824 gtk_widget_show(GTK_WIDGET(tb));
826 sigc::connection* conn = new sigc::connection;
827 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
829 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
830 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
832 return hb;
833 }
835 GtkWidget *
836 sp_aux_toolbox_new()
837 {
838 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
840 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
842 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
844 gtk_widget_set_sensitive(tb, FALSE);
846 GtkWidget *hb = gtk_handle_box_new();
847 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
848 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
849 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
851 gtk_container_add(GTK_CONTAINER(hb), tb);
852 gtk_widget_show(GTK_WIDGET(tb));
854 sigc::connection* conn = new sigc::connection;
855 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
857 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
858 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
860 return hb;
861 }
863 //####################################
864 //# Commands Bar
865 //####################################
867 GtkWidget *
868 sp_commands_toolbox_new()
869 {
870 GtkWidget *tb = gtk_toolbar_new();
872 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
873 gtk_widget_set_sensitive(tb, FALSE);
875 GtkWidget *hb = gtk_handle_box_new();
876 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
877 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
878 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
880 gtk_container_add(GTK_CONTAINER(hb), tb);
881 gtk_widget_show(GTK_WIDGET(tb));
883 sigc::connection* conn = new sigc::connection;
884 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
886 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
887 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
889 return hb;
890 }
893 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
894 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
895 gchar const *path, gchar const *data, gdouble def,
896 GtkWidget *focusTarget,
897 GtkWidget *us,
898 GObject *dataKludge,
899 gboolean altx, gchar const *altx_mark,
900 gdouble lower, gdouble upper, gdouble step, gdouble page,
901 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
902 void (*callback)(GtkAdjustment *, GObject *),
903 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
904 {
905 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
906 lower, upper, step, page, page ) );
907 if (us) {
908 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
909 }
911 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
913 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
914 if ( shortLabel ) {
915 g_object_set( act, "short_label", shortLabel, NULL );
916 }
918 if ( (descrCount > 0) && descrLabels && descrValues ) {
919 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
920 }
922 if ( focusTarget ) {
923 ege_adjustment_action_set_focuswidget( act, focusTarget );
924 }
926 if ( altx && altx_mark ) {
927 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
928 }
930 if ( dataKludge ) {
931 g_object_set_data( dataKludge, data, adj );
932 }
934 // Using a cast just to make sure we pass in the right kind of function pointer
935 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
937 return act;
938 }
941 //####################################
942 //# node editing callbacks
943 //####################################
945 /**
946 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
947 */
948 static ShapeEditor *get_current_shape_editor()
949 {
950 if (!SP_ACTIVE_DESKTOP) {
951 return NULL;
952 }
954 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
956 if (!SP_IS_NODE_CONTEXT(event_context)) {
957 return NULL;
958 }
960 return SP_NODE_CONTEXT(event_context)->shape_editor;
961 }
964 void
965 sp_node_path_edit_add(void)
966 {
967 ShapeEditor *shape_editor = get_current_shape_editor();
968 if (shape_editor) shape_editor->add_node();
969 }
971 void
972 sp_node_path_edit_delete(void)
973 {
974 ShapeEditor *shape_editor = get_current_shape_editor();
975 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
976 }
978 void
979 sp_node_path_edit_delete_segment(void)
980 {
981 ShapeEditor *shape_editor = get_current_shape_editor();
982 if (shape_editor) shape_editor->delete_segment();
983 }
985 void
986 sp_node_path_edit_break(void)
987 {
988 ShapeEditor *shape_editor = get_current_shape_editor();
989 if (shape_editor) shape_editor->break_at_nodes();
990 }
992 void
993 sp_node_path_edit_join(void)
994 {
995 ShapeEditor *shape_editor = get_current_shape_editor();
996 if (shape_editor) shape_editor->join_nodes();
997 }
999 void
1000 sp_node_path_edit_join_segment(void)
1001 {
1002 ShapeEditor *shape_editor = get_current_shape_editor();
1003 if (shape_editor) shape_editor->join_segments();
1004 }
1006 void
1007 sp_node_path_edit_toline(void)
1008 {
1009 ShapeEditor *shape_editor = get_current_shape_editor();
1010 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1011 }
1013 void
1014 sp_node_path_edit_tocurve(void)
1015 {
1016 ShapeEditor *shape_editor = get_current_shape_editor();
1017 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1018 }
1020 void
1021 sp_node_path_edit_cusp(void)
1022 {
1023 ShapeEditor *shape_editor = get_current_shape_editor();
1024 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1025 }
1027 void
1028 sp_node_path_edit_smooth(void)
1029 {
1030 ShapeEditor *shape_editor = get_current_shape_editor();
1031 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1032 }
1034 void
1035 sp_node_path_edit_symmetrical(void)
1036 {
1037 ShapeEditor *shape_editor = get_current_shape_editor();
1038 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1039 }
1041 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1042 bool show = gtk_toggle_action_get_active( act );
1043 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1044 ShapeEditor *shape_editor = get_current_shape_editor();
1045 if (shape_editor) shape_editor->show_handles(show);
1046 }
1048 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1049 bool show = gtk_toggle_action_get_active( act );
1050 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1051 ShapeEditor *shape_editor = get_current_shape_editor();
1052 if (shape_editor) shape_editor->show_helperpath(show);
1053 }
1055 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1056 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1057 }
1059 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1060 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1061 }
1063 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1064 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1065 }
1067 /* is called when the node selection is modified */
1068 static void
1069 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1070 {
1071 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1072 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1073 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1074 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1076 // quit if run by the attr_changed listener
1077 if (g_object_get_data( tbl, "freeze" )) {
1078 return;
1079 }
1081 // in turn, prevent listener from responding
1082 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1084 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1085 SPUnit const *unit = tracker->getActiveUnit();
1087 ShapeEditor *shape_editor = get_current_shape_editor();
1088 if (shape_editor && shape_editor->has_nodepath()) {
1089 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1090 int n_selected = 0;
1091 if (nodepath) {
1092 n_selected = nodepath->numSelected();
1093 }
1095 if (n_selected == 0) {
1096 gtk_action_set_sensitive(xact, FALSE);
1097 gtk_action_set_sensitive(yact, FALSE);
1098 } else {
1099 gtk_action_set_sensitive(xact, TRUE);
1100 gtk_action_set_sensitive(yact, TRUE);
1101 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1102 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1104 if (n_selected == 1) {
1105 NR::Point sel_node = nodepath->singleSelectedCoords();
1106 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1107 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1108 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1109 }
1110 } else {
1111 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1112 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1113 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1114 /* Note: Currently x and y will always have a value, even if the coordinates of the
1115 selected nodes don't coincide (in this case we use the coordinates of the center
1116 of the bounding box). So the entries are never set to zero. */
1117 // FIXME: Maybe we should clear the entry if several nodes are selected
1118 // instead of providing a kind of average value
1119 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1120 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1121 }
1122 }
1123 }
1124 } else {
1125 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1126 gtk_action_set_sensitive(xact, FALSE);
1127 gtk_action_set_sensitive(yact, FALSE);
1128 }
1130 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1131 }
1133 static void
1134 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1135 {
1136 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1138 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1139 SPUnit const *unit = tracker->getActiveUnit();
1141 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1142 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1143 }
1145 // quit if run by the attr_changed listener
1146 if (g_object_get_data( tbl, "freeze" )) {
1147 return;
1148 }
1150 // in turn, prevent listener from responding
1151 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1153 ShapeEditor *shape_editor = get_current_shape_editor();
1154 if (shape_editor && shape_editor->has_nodepath()) {
1155 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1156 if (!strcmp(value_name, "x")) {
1157 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1158 }
1159 if (!strcmp(value_name, "y")) {
1160 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1161 }
1162 }
1164 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1165 }
1167 static void
1168 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1169 {
1170 sp_node_path_value_changed(adj, tbl, "x");
1171 }
1173 static void
1174 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1175 {
1176 sp_node_path_value_changed(adj, tbl, "y");
1177 }
1179 void
1180 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1181 {
1182 {
1183 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1184 SPItem *item = selection->singleItem();
1185 if (item && SP_IS_LPE_ITEM(item)) {
1186 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1187 gtk_action_set_sensitive(w, TRUE);
1188 } else {
1189 gtk_action_set_sensitive(w, FALSE);
1190 }
1191 } else {
1192 gtk_action_set_sensitive(w, FALSE);
1193 }
1194 }
1196 {
1197 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1198 SPItem *item = selection->singleItem();
1199 if (item && item->clip_ref && item->clip_ref->getObject()) {
1200 gtk_action_set_sensitive(w, TRUE);
1201 } else {
1202 gtk_action_set_sensitive(w, FALSE);
1203 }
1204 }
1206 {
1207 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1208 SPItem *item = selection->singleItem();
1209 if (item && item->mask_ref && item->mask_ref->getObject()) {
1210 gtk_action_set_sensitive(w, TRUE);
1211 } else {
1212 gtk_action_set_sensitive(w, FALSE);
1213 }
1214 }
1215 }
1217 void
1218 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1219 {
1220 sp_node_toolbox_sel_changed (selection, tbl);
1221 }
1225 //################################
1226 //## Node Editing Toolbox ##
1227 //################################
1229 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1230 {
1231 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1232 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1233 g_object_set_data( holder, "tracker", tracker );
1235 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1237 {
1238 InkAction* inky = ink_action_new( "NodeInsertAction",
1239 _("Insert node"),
1240 _("Insert new nodes into selected segments"),
1241 "node_insert",
1242 secondarySize );
1243 g_object_set( inky, "short_label", _("Insert"), NULL );
1244 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1245 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1246 }
1248 {
1249 InkAction* inky = ink_action_new( "NodeDeleteAction",
1250 _("Delete node"),
1251 _("Delete selected nodes"),
1252 "node_delete",
1253 secondarySize );
1254 g_object_set( inky, "short_label", _("Delete"), NULL );
1255 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1256 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1257 }
1259 {
1260 InkAction* inky = ink_action_new( "NodeJoinAction",
1261 _("Join endnodes"),
1262 _("Join selected endnodes"),
1263 "node_join",
1264 secondarySize );
1265 g_object_set( inky, "short_label", _("Join"), NULL );
1266 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1267 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1268 }
1270 {
1271 InkAction* inky = ink_action_new( "NodeBreakAction",
1272 _("Break nodes"),
1273 _("Break path at selected nodes"),
1274 "node_break",
1275 secondarySize );
1276 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1277 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1278 }
1281 {
1282 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1283 _("Join with segment"),
1284 _("Join selected endnodes with a new segment"),
1285 "node_join_segment",
1286 secondarySize );
1287 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1288 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1289 }
1291 {
1292 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1293 _("Delete segment"),
1294 _("Delete segment between two non-endpoint nodes"),
1295 "node_delete_segment",
1296 secondarySize );
1297 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1298 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299 }
1301 {
1302 InkAction* inky = ink_action_new( "NodeCuspAction",
1303 _("Node Cusp"),
1304 _("Make selected nodes corner"),
1305 "node_cusp",
1306 secondarySize );
1307 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1308 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1309 }
1311 {
1312 InkAction* inky = ink_action_new( "NodeSmoothAction",
1313 _("Node Smooth"),
1314 _("Make selected nodes smooth"),
1315 "node_smooth",
1316 secondarySize );
1317 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1318 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1319 }
1321 {
1322 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1323 _("Node Symmetric"),
1324 _("Make selected nodes symmetric"),
1325 "node_symmetric",
1326 secondarySize );
1327 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1328 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1329 }
1331 {
1332 InkAction* inky = ink_action_new( "NodeLineAction",
1333 _("Node Line"),
1334 _("Make selected segments lines"),
1335 "node_line",
1336 secondarySize );
1337 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1338 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339 }
1341 {
1342 InkAction* inky = ink_action_new( "NodeCurveAction",
1343 _("Node Curve"),
1344 _("Make selected segments curves"),
1345 "node_curve",
1346 secondarySize );
1347 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1348 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1349 }
1351 {
1352 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1353 _("Show Handles"),
1354 _("Show the Bezier handles of selected nodes"),
1355 "nodes_show_handles",
1356 Inkscape::ICON_SIZE_DECORATION );
1357 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1358 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1359 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1360 }
1362 {
1363 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1364 _("Show Outline"),
1365 _("Show the outline of the path"),
1366 "nodes_show_helperpath",
1367 Inkscape::ICON_SIZE_DECORATION );
1368 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1369 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1370 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1371 }
1373 {
1374 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1375 _("Next path effect parameter"),
1376 _("Show next path effect parameter for editing"),
1377 "edit_next_parameter",
1378 Inkscape::ICON_SIZE_DECORATION );
1379 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1380 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1381 g_object_set_data( holder, "nodes_lpeedit", inky);
1382 }
1384 {
1385 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1386 _("Edit clipping path"),
1387 _("Edit the clipping path of the object"),
1388 "nodeedit-clippath",
1389 Inkscape::ICON_SIZE_DECORATION );
1390 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1391 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1392 g_object_set_data( holder, "nodes_clippathedit", inky);
1393 }
1395 {
1396 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1397 _("Edit mask path"),
1398 _("Edit the mask of the object"),
1399 "nodeedit-mask",
1400 Inkscape::ICON_SIZE_DECORATION );
1401 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1402 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1403 g_object_set_data( holder, "nodes_maskedit", inky);
1404 }
1406 /* X coord of selected node(s) */
1407 {
1408 EgeAdjustmentAction* eact = 0;
1409 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1410 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1411 eact = create_adjustment_action( "NodeXAction",
1412 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1413 "tools.nodes", "Xcoord", 0,
1414 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1415 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1416 labels, values, G_N_ELEMENTS(labels),
1417 sp_node_path_x_value_changed );
1418 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1419 g_object_set_data( holder, "nodes_x_action", eact );
1420 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1421 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1422 }
1424 /* Y coord of selected node(s) */
1425 {
1426 EgeAdjustmentAction* eact = 0;
1427 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1428 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1429 eact = create_adjustment_action( "NodeYAction",
1430 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1431 "tools.nodes", "Ycoord", 0,
1432 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1433 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1434 labels, values, G_N_ELEMENTS(labels),
1435 sp_node_path_y_value_changed );
1436 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1437 g_object_set_data( holder, "nodes_y_action", eact );
1438 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1439 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1440 }
1442 // add the units menu
1443 {
1444 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1445 gtk_action_group_add_action( mainActions, act );
1446 }
1449 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1451 //watch selection
1452 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1454 sigc::connection *c_selection_changed =
1455 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1456 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1457 pool->add_connection ("selection-changed", c_selection_changed);
1459 sigc::connection *c_selection_modified =
1460 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1461 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1462 pool->add_connection ("selection-modified", c_selection_modified);
1464 sigc::connection *c_subselection_changed =
1465 new sigc::connection (desktop->connectToolSubselectionChanged
1466 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1467 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1469 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1471 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1472 } // end of sp_node_toolbox_prep()
1475 //########################
1476 //## Zoom Toolbox ##
1477 //########################
1479 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1480 {
1481 // no custom GtkAction setup needed
1482 } // end of sp_zoom_toolbox_prep()
1484 void
1485 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1486 {
1487 toolbox_set_desktop(toolbox,
1488 desktop,
1489 setup_tool_toolbox,
1490 update_tool_toolbox,
1491 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1492 "event_context_connection")));
1493 }
1496 void
1497 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1498 {
1499 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1500 desktop,
1501 setup_aux_toolbox,
1502 update_aux_toolbox,
1503 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1504 "event_context_connection")));
1505 }
1507 void
1508 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1509 {
1510 toolbox_set_desktop(toolbox,
1511 desktop,
1512 setup_commands_toolbox,
1513 update_commands_toolbox,
1514 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1515 "event_context_connection")));
1516 }
1518 static void
1519 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1520 {
1521 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1522 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1524 if (old_desktop) {
1525 GList *children, *iter;
1527 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1528 for ( iter = children ; iter ; iter = iter->next ) {
1529 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1530 }
1531 g_list_free(children);
1532 }
1534 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1536 if (desktop) {
1537 gtk_widget_set_sensitive(toolbox, TRUE);
1538 setup_func(toolbox, desktop);
1539 update_func(desktop, desktop->event_context, toolbox);
1540 *conn = desktop->connectEventContextChanged
1541 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1542 } else {
1543 gtk_widget_set_sensitive(toolbox, FALSE);
1544 }
1546 } // end of toolbox_set_desktop()
1549 static void
1550 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1551 {
1552 gchar const * descr =
1553 "<ui>"
1554 " <toolbar name='ToolToolbar'>"
1555 " <toolitem action='ToolSelector' />"
1556 " <toolitem action='ToolNode' />"
1557 " <toolitem action='ToolTweak' />"
1558 " <toolitem action='ToolZoom' />"
1559 " <toolitem action='ToolRect' />"
1560 " <toolitem action='Tool3DBox' />"
1561 " <toolitem action='ToolArc' />"
1562 " <toolitem action='ToolStar' />"
1563 " <toolitem action='ToolSpiral' />"
1564 " <toolitem action='ToolPencil' />"
1565 " <toolitem action='ToolPen' />"
1566 " <toolitem action='ToolCalligraphic' />"
1567 " <toolitem action='ToolEraser' />"
1568 " <toolitem action='ToolPaintBucket' />"
1569 " <toolitem action='ToolText' />"
1570 " <toolitem action='ToolConnector' />"
1571 " <toolitem action='ToolGradient' />"
1572 " <toolitem action='ToolDropper' />"
1573 " </toolbar>"
1574 "</ui>";
1575 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1576 GtkUIManager* mgr = gtk_ui_manager_new();
1577 GError* errVal = 0;
1579 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1580 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1582 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1583 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1584 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1585 }
1586 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1587 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1589 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1590 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1592 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1594 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1595 if ( child ) {
1596 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1597 }
1599 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1600 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1601 }
1604 static void
1605 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1606 {
1607 gchar const *const tname = ( eventcontext
1608 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1609 : NULL );
1610 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1612 for (int i = 0 ; tools[i].type_name ; i++ ) {
1613 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1614 if ( act ) {
1615 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1616 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1617 if ( verbAct ) {
1618 verbAct->set_active(setActive);
1619 }
1620 }
1621 }
1622 }
1624 static void
1625 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1626 {
1627 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1628 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1629 GtkUIManager* mgr = gtk_ui_manager_new();
1630 GError* errVal = 0;
1631 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1632 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1634 std::map<std::string, GtkWidget*> dataHolders;
1636 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1637 if ( aux_toolboxes[i].prep_func ) {
1638 // converted to GtkActions and UIManager
1640 GtkWidget* kludge = gtk_toolbar_new();
1641 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1642 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1643 dataHolders[aux_toolboxes[i].type_name] = kludge;
1644 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1645 } else {
1647 GtkWidget *sub_toolbox = 0;
1648 if (aux_toolboxes[i].create_func == NULL)
1649 sub_toolbox = sp_empty_toolbox_new(desktop);
1650 else {
1651 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1652 }
1654 gtk_size_group_add_widget( grouper, sub_toolbox );
1656 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1657 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1659 }
1660 }
1662 // Second pass to create toolbars *after* all GtkActions are created
1663 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1664 if ( aux_toolboxes[i].prep_func ) {
1665 // converted to GtkActions and UIManager
1667 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1669 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1670 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1672 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1673 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1674 g_free( tmp );
1675 tmp = 0;
1677 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1678 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1679 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1680 }
1681 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1684 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1686 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1687 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1688 swatch->setDesktop( desktop );
1689 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1690 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1691 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1692 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 );
1693 }
1695 gtk_widget_show_all( holder );
1696 sp_set_font_size_smaller( holder );
1698 gtk_size_group_add_widget( grouper, holder );
1700 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1701 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1702 }
1703 }
1705 g_object_unref( G_OBJECT(grouper) );
1706 }
1708 static void
1709 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1710 {
1711 gchar const *tname = ( eventcontext
1712 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1713 : NULL );
1714 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1715 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1716 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1717 gtk_widget_show_all(sub_toolbox);
1718 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1719 } else {
1720 gtk_widget_hide(sub_toolbox);
1721 }
1722 }
1723 }
1725 static void
1726 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1727 {
1728 gchar const * descr =
1729 "<ui>"
1730 " <toolbar name='CommandsToolbar'>"
1731 " <toolitem action='FileNew' />"
1732 " <toolitem action='FileOpen' />"
1733 " <toolitem action='FileSave' />"
1734 " <toolitem action='FilePrint' />"
1735 " <separator />"
1736 " <toolitem action='FileImport' />"
1737 " <toolitem action='FileExport' />"
1738 " <separator />"
1739 " <toolitem action='EditUndo' />"
1740 " <toolitem action='EditRedo' />"
1741 " <separator />"
1742 " <toolitem action='EditCopy' />"
1743 " <toolitem action='EditCut' />"
1744 " <toolitem action='EditPaste' />"
1745 " <separator />"
1746 " <toolitem action='ZoomSelection' />"
1747 " <toolitem action='ZoomDrawing' />"
1748 " <toolitem action='ZoomPage' />"
1749 " <separator />"
1750 " <toolitem action='EditDuplicate' />"
1751 " <toolitem action='EditClone' />"
1752 " <toolitem action='EditUnlinkClone' />"
1753 " <separator />"
1754 " <toolitem action='SelectionGroup' />"
1755 " <toolitem action='SelectionUnGroup' />"
1756 " <separator />"
1757 " <toolitem action='DialogFillStroke' />"
1758 " <toolitem action='DialogText' />"
1759 " <toolitem action='DialogXMLEditor' />"
1760 " <toolitem action='DialogAlignDistribute' />"
1761 " <separator />"
1762 " <toolitem action='DialogPreferences' />"
1763 " <toolitem action='DialogDocumentProperties' />"
1764 " </toolbar>"
1765 "</ui>";
1766 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1769 GtkUIManager* mgr = gtk_ui_manager_new();
1770 GError* errVal = 0;
1772 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1773 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1775 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1776 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1777 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1778 }
1780 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1781 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1783 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1784 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1787 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1789 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1790 if ( child ) {
1791 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1792 }
1794 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1795 }
1797 static void
1798 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1799 {
1800 }
1802 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1803 {
1804 gtk_widget_show(toolbox_toplevel);
1805 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1807 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1808 if (!shown_toolbox) {
1809 return;
1810 }
1811 gtk_widget_show(toolbox);
1813 gtk_widget_show_all(shown_toolbox);
1814 }
1816 static GtkWidget *
1817 sp_empty_toolbox_new(SPDesktop *desktop)
1818 {
1819 GtkWidget *tbl = gtk_toolbar_new();
1820 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1821 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1823 gtk_widget_show_all(tbl);
1824 sp_set_font_size_smaller (tbl);
1826 return tbl;
1827 }
1829 #define MODE_LABEL_WIDTH 70
1831 //########################
1832 //## Star ##
1833 //########################
1835 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1836 {
1837 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1839 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1840 // do not remember prefs if this call is initiated by an undo change, because undoing object
1841 // creation sets bogus values to its attributes before it is deleted
1842 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1843 }
1845 // quit if run by the attr_changed listener
1846 if (g_object_get_data( dataKludge, "freeze" )) {
1847 return;
1848 }
1850 // in turn, prevent listener from responding
1851 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1853 bool modmade = false;
1855 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1856 GSList const *items = selection->itemList();
1857 for (; items != NULL; items = items->next) {
1858 if (SP_IS_STAR((SPItem *) items->data)) {
1859 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1860 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1861 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1862 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1863 + M_PI / (gint)adj->value));
1864 SP_OBJECT((SPItem *) items->data)->updateRepr();
1865 modmade = true;
1866 }
1867 }
1868 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1869 _("Star: Change number of corners"));
1871 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1872 }
1874 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1875 {
1876 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1878 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1879 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1880 }
1882 // quit if run by the attr_changed listener
1883 if (g_object_get_data( dataKludge, "freeze" )) {
1884 return;
1885 }
1887 // in turn, prevent listener from responding
1888 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1890 bool modmade = false;
1891 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1892 GSList const *items = selection->itemList();
1893 for (; items != NULL; items = items->next) {
1894 if (SP_IS_STAR((SPItem *) items->data)) {
1895 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1897 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1898 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1899 if (r2 < r1) {
1900 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1901 } else {
1902 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1903 }
1905 SP_OBJECT((SPItem *) items->data)->updateRepr();
1906 modmade = true;
1907 }
1908 }
1910 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1911 _("Star: Change spoke ratio"));
1913 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1914 }
1916 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1917 {
1918 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1919 bool flat = ege_select_one_action_get_active( act ) == 0;
1921 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1922 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1923 flat ? "true" : "false" );
1924 }
1926 // quit if run by the attr_changed listener
1927 if (g_object_get_data( dataKludge, "freeze" )) {
1928 return;
1929 }
1931 // in turn, prevent listener from responding
1932 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1934 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1935 GSList const *items = selection->itemList();
1936 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1937 bool modmade = false;
1939 if ( prop_action ) {
1940 gtk_action_set_sensitive( prop_action, !flat );
1941 }
1943 for (; items != NULL; items = items->next) {
1944 if (SP_IS_STAR((SPItem *) items->data)) {
1945 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1946 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1947 SP_OBJECT((SPItem *) items->data)->updateRepr();
1948 modmade = true;
1949 }
1950 }
1952 if (modmade) {
1953 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1954 flat ? _("Make polygon") : _("Make star"));
1955 }
1957 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1958 }
1960 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1961 {
1962 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1964 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1965 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1966 }
1968 // quit if run by the attr_changed listener
1969 if (g_object_get_data( dataKludge, "freeze" )) {
1970 return;
1971 }
1973 // in turn, prevent listener from responding
1974 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1976 bool modmade = false;
1978 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1979 GSList const *items = selection->itemList();
1980 for (; items != NULL; items = items->next) {
1981 if (SP_IS_STAR((SPItem *) items->data)) {
1982 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1983 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1984 SP_OBJECT(items->data)->updateRepr();
1985 modmade = true;
1986 }
1987 }
1988 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1989 _("Star: Change rounding"));
1991 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1992 }
1994 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1995 {
1996 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1998 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1999 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2000 }
2002 // quit if run by the attr_changed listener
2003 if (g_object_get_data( dataKludge, "freeze" )) {
2004 return;
2005 }
2007 // in turn, prevent listener from responding
2008 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2010 bool modmade = false;
2012 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2013 GSList const *items = selection->itemList();
2014 for (; items != NULL; items = items->next) {
2015 if (SP_IS_STAR((SPItem *) items->data)) {
2016 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2017 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2018 SP_OBJECT(items->data)->updateRepr();
2019 modmade = true;
2020 }
2021 }
2022 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2023 _("Star: Change randomization"));
2025 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2026 }
2029 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2030 gchar const */*old_value*/, gchar const */*new_value*/,
2031 bool /*is_interactive*/, gpointer data)
2032 {
2033 GtkWidget *tbl = GTK_WIDGET(data);
2035 // quit if run by the _changed callbacks
2036 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2037 return;
2038 }
2040 // in turn, prevent callbacks from responding
2041 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2043 GtkAdjustment *adj = 0;
2045 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2046 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2048 if (!strcmp(name, "inkscape:randomized")) {
2049 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2050 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2051 } else if (!strcmp(name, "inkscape:rounded")) {
2052 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2053 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2054 } else if (!strcmp(name, "inkscape:flatsided")) {
2055 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2056 char const *flatsides = repr->attribute("inkscape:flatsided");
2057 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2058 if ( flatsides && !strcmp(flatsides,"false") ) {
2059 ege_select_one_action_set_active( flat_action, 1 );
2060 gtk_action_set_sensitive( prop_action, TRUE );
2061 } else {
2062 ege_select_one_action_set_active( flat_action, 0 );
2063 gtk_action_set_sensitive( prop_action, FALSE );
2064 }
2065 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2066 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2067 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2068 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2069 if (r2 < r1) {
2070 gtk_adjustment_set_value(adj, r2/r1);
2071 } else {
2072 gtk_adjustment_set_value(adj, r1/r2);
2073 }
2074 } else if (!strcmp(name, "sodipodi:sides")) {
2075 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2076 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2077 }
2079 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2080 }
2083 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2084 {
2085 NULL, /* child_added */
2086 NULL, /* child_removed */
2087 star_tb_event_attr_changed,
2088 NULL, /* content_changed */
2089 NULL /* order_changed */
2090 };
2093 /**
2094 * \param selection Should not be NULL.
2095 */
2096 static void
2097 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2098 {
2099 int n_selected = 0;
2100 Inkscape::XML::Node *repr = NULL;
2102 purge_repr_listener( tbl, tbl );
2104 for (GSList const *items = selection->itemList();
2105 items != NULL;
2106 items = items->next)
2107 {
2108 if (SP_IS_STAR((SPItem *) items->data)) {
2109 n_selected++;
2110 repr = SP_OBJECT_REPR((SPItem *) items->data);
2111 }
2112 }
2114 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2116 if (n_selected == 0) {
2117 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2118 } else if (n_selected == 1) {
2119 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2121 if (repr) {
2122 g_object_set_data( tbl, "repr", repr );
2123 Inkscape::GC::anchor(repr);
2124 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2125 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2126 }
2127 } else {
2128 // FIXME: implement averaging of all parameters for multiple selected stars
2129 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2130 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2131 }
2132 }
2135 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2136 {
2137 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2138 // callbacks to lump all the changes for all selected objects in one undo step
2140 GtkAdjustment *adj = 0;
2142 // fixme: make settable in prefs!
2143 gint mag = 5;
2144 gdouble prop = 0.5;
2145 gboolean flat = FALSE;
2146 gdouble randomized = 0;
2147 gdouble rounded = 0;
2149 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2150 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2152 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2153 gtk_action_set_sensitive( sb2, !flat );
2155 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2156 gtk_adjustment_set_value(adj, mag);
2157 gtk_adjustment_value_changed(adj);
2159 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2160 gtk_adjustment_set_value(adj, prop);
2161 gtk_adjustment_value_changed(adj);
2163 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2164 gtk_adjustment_set_value(adj, rounded);
2165 gtk_adjustment_value_changed(adj);
2167 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2168 gtk_adjustment_set_value(adj, randomized);
2169 gtk_adjustment_value_changed(adj);
2170 }
2173 void
2174 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2175 {
2176 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2177 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2178 GtkWidget *l = gtk_label_new(NULL);
2179 gtk_label_set_markup(GTK_LABEL(l), title);
2180 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2181 if ( GTK_IS_TOOLBAR(tbl) ) {
2182 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2183 } else {
2184 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2185 }
2186 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2187 }
2190 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2191 {
2192 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2194 {
2195 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2196 ege_output_action_set_use_markup( act, TRUE );
2197 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2198 g_object_set_data( holder, "mode_action", act );
2199 }
2201 {
2202 EgeAdjustmentAction* eact = 0;
2203 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2204 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2206 /* Flatsided checkbox */
2207 {
2208 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2210 GtkTreeIter iter;
2211 gtk_list_store_append( model, &iter );
2212 gtk_list_store_set( model, &iter,
2213 0, _("Polygon"),
2214 1, _("Regular polygon (with one handle) instead of a star"),
2215 2, "star_flat",
2216 -1 );
2218 gtk_list_store_append( model, &iter );
2219 gtk_list_store_set( model, &iter,
2220 0, _("Star"),
2221 1, _("Star instead of a regular polygon (with one handle)"),
2222 2, "star_angled",
2223 -1 );
2225 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2226 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2227 g_object_set_data( holder, "flat_action", act );
2229 ege_select_one_action_set_appearance( act, "full" );
2230 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2231 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2232 ege_select_one_action_set_icon_column( act, 2 );
2233 ege_select_one_action_set_icon_size( act, secondarySize );
2234 ege_select_one_action_set_tooltip_column( act, 1 );
2236 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2237 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2238 }
2240 /* Magnitude */
2241 {
2242 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2243 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2244 eact = create_adjustment_action( "MagnitudeAction",
2245 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2246 "tools.shapes.star", "magnitude", 3,
2247 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2248 3, 1024, 1, 5,
2249 labels, values, G_N_ELEMENTS(labels),
2250 sp_stb_magnitude_value_changed,
2251 1.0, 0 );
2252 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2253 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2254 }
2256 /* Spoke ratio */
2257 {
2258 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2259 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2260 eact = create_adjustment_action( "SpokeAction",
2261 _("Spoke ratio"), _("Spoke ratio:"),
2262 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2263 // Base radius is the same for the closest handle.
2264 _("Base radius to tip radius ratio"),
2265 "tools.shapes.star", "proportion", 0.5,
2266 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2267 0.01, 1.0, 0.01, 0.1,
2268 labels, values, G_N_ELEMENTS(labels),
2269 sp_stb_proportion_value_changed );
2270 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2271 g_object_set_data( holder, "prop_action", eact );
2272 }
2274 if ( !isFlatSided ) {
2275 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2276 } else {
2277 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2278 }
2280 /* Roundedness */
2281 {
2282 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2283 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2284 eact = create_adjustment_action( "RoundednessAction",
2285 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2286 "tools.shapes.star", "rounded", 0.0,
2287 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2288 -10.0, 10.0, 0.01, 0.1,
2289 labels, values, G_N_ELEMENTS(labels),
2290 sp_stb_rounded_value_changed );
2291 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2292 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2293 }
2295 /* Randomization */
2296 {
2297 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2298 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2299 eact = create_adjustment_action( "RandomizationAction",
2300 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2301 "tools.shapes.star", "randomized", 0.0,
2302 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2303 -10.0, 10.0, 0.001, 0.01,
2304 labels, values, G_N_ELEMENTS(labels),
2305 sp_stb_randomized_value_changed, 0.1, 3 );
2306 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2307 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2308 }
2309 }
2311 {
2312 /* Reset */
2313 {
2314 GtkAction* act = gtk_action_new( "StarResetAction",
2315 _("Defaults"),
2316 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2317 GTK_STOCK_CLEAR );
2318 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2319 gtk_action_group_add_action( mainActions, act );
2320 gtk_action_set_sensitive( act, TRUE );
2321 }
2322 }
2324 sigc::connection *connection = new sigc::connection(
2325 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2326 );
2327 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2328 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2329 }
2332 //########################
2333 //## Rect ##
2334 //########################
2336 static void sp_rtb_sensitivize( GObject *tbl )
2337 {
2338 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2339 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2340 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2342 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2343 gtk_action_set_sensitive( not_rounded, FALSE );
2344 } else {
2345 gtk_action_set_sensitive( not_rounded, TRUE );
2346 }
2347 }
2350 static void
2351 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2352 void (*setter)(SPRect *, gdouble))
2353 {
2354 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2356 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2357 SPUnit const *unit = tracker->getActiveUnit();
2359 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2360 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2361 }
2363 // quit if run by the attr_changed listener
2364 if (g_object_get_data( tbl, "freeze" )) {
2365 return;
2366 }
2368 // in turn, prevent listener from responding
2369 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2371 bool modmade = false;
2372 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2373 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2374 if (SP_IS_RECT(items->data)) {
2375 if (adj->value != 0) {
2376 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2377 } else {
2378 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2379 }
2380 modmade = true;
2381 }
2382 }
2384 sp_rtb_sensitivize( tbl );
2386 if (modmade) {
2387 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2388 _("Change rectangle"));
2389 }
2391 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2392 }
2394 static void
2395 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2396 {
2397 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2398 }
2400 static void
2401 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2402 {
2403 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2404 }
2406 static void
2407 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2408 {
2409 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2410 }
2412 static void
2413 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2414 {
2415 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2416 }
2420 static void
2421 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2422 {
2423 GtkAdjustment *adj = 0;
2425 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2426 gtk_adjustment_set_value(adj, 0.0);
2427 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2428 gtk_adjustment_value_changed(adj);
2430 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2431 gtk_adjustment_set_value(adj, 0.0);
2432 gtk_adjustment_value_changed(adj);
2434 sp_rtb_sensitivize( obj );
2435 }
2437 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2438 gchar const */*old_value*/, gchar const */*new_value*/,
2439 bool /*is_interactive*/, gpointer data)
2440 {
2441 GObject *tbl = G_OBJECT(data);
2443 // quit if run by the _changed callbacks
2444 if (g_object_get_data( tbl, "freeze" )) {
2445 return;
2446 }
2448 // in turn, prevent callbacks from responding
2449 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2451 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2452 SPUnit const *unit = tracker->getActiveUnit();
2454 gpointer item = g_object_get_data( tbl, "item" );
2455 if (item && SP_IS_RECT(item)) {
2456 {
2457 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2458 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2459 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2460 }
2462 {
2463 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2464 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2465 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2466 }
2468 {
2469 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2470 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2471 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2472 }
2474 {
2475 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2476 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2477 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2478 }
2479 }
2481 sp_rtb_sensitivize( tbl );
2483 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2484 }
2487 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2488 NULL, /* child_added */
2489 NULL, /* child_removed */
2490 rect_tb_event_attr_changed,
2491 NULL, /* content_changed */
2492 NULL /* order_changed */
2493 };
2495 /**
2496 * \param selection should not be NULL.
2497 */
2498 static void
2499 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2500 {
2501 int n_selected = 0;
2502 Inkscape::XML::Node *repr = NULL;
2503 SPItem *item = NULL;
2505 if ( g_object_get_data( tbl, "repr" ) ) {
2506 g_object_set_data( tbl, "item", NULL );
2507 }
2508 purge_repr_listener( tbl, tbl );
2510 for (GSList const *items = selection->itemList();
2511 items != NULL;
2512 items = items->next) {
2513 if (SP_IS_RECT((SPItem *) items->data)) {
2514 n_selected++;
2515 item = (SPItem *) items->data;
2516 repr = SP_OBJECT_REPR(item);
2517 }
2518 }
2520 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2522 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2524 if (n_selected == 0) {
2525 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2527 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2528 gtk_action_set_sensitive(w, FALSE);
2529 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2530 gtk_action_set_sensitive(h, FALSE);
2532 } else if (n_selected == 1) {
2533 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2534 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2536 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2537 gtk_action_set_sensitive(w, TRUE);
2538 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2539 gtk_action_set_sensitive(h, TRUE);
2541 if (repr) {
2542 g_object_set_data( tbl, "repr", repr );
2543 g_object_set_data( tbl, "item", item );
2544 Inkscape::GC::anchor(repr);
2545 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2546 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2547 }
2548 } else {
2549 // FIXME: implement averaging of all parameters for multiple selected
2550 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2551 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2552 sp_rtb_sensitivize( tbl );
2553 }
2554 }
2557 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2558 {
2559 EgeAdjustmentAction* eact = 0;
2560 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2562 {
2563 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2564 ege_output_action_set_use_markup( act, TRUE );
2565 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2566 g_object_set_data( holder, "mode_action", act );
2567 }
2569 // rx/ry units menu: create
2570 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2571 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2572 // fixme: add % meaning per cent of the width/height
2573 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2574 g_object_set_data( holder, "tracker", tracker );
2576 /* W */
2577 {
2578 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2579 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2580 eact = create_adjustment_action( "RectWidthAction",
2581 _("Width"), _("W:"), _("Width of rectangle"),
2582 "tools.shapes.rect", "width", 0,
2583 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2584 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2585 labels, values, G_N_ELEMENTS(labels),
2586 sp_rtb_width_value_changed );
2587 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2588 g_object_set_data( holder, "width_action", eact );
2589 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2590 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2591 }
2593 /* H */
2594 {
2595 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2596 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2597 eact = create_adjustment_action( "RectHeightAction",
2598 _("Height"), _("H:"), _("Height of rectangle"),
2599 "tools.shapes.rect", "height", 0,
2600 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2601 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2602 labels, values, G_N_ELEMENTS(labels),
2603 sp_rtb_height_value_changed );
2604 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2605 g_object_set_data( holder, "height_action", eact );
2606 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2607 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2608 }
2610 /* rx */
2611 {
2612 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2613 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2614 eact = create_adjustment_action( "RadiusXAction",
2615 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2616 "tools.shapes.rect", "rx", 0,
2617 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2618 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2619 labels, values, G_N_ELEMENTS(labels),
2620 sp_rtb_rx_value_changed);
2621 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2622 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2623 }
2625 /* ry */
2626 {
2627 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2628 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2629 eact = create_adjustment_action( "RadiusYAction",
2630 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2631 "tools.shapes.rect", "ry", 0,
2632 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2633 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2634 labels, values, G_N_ELEMENTS(labels),
2635 sp_rtb_ry_value_changed);
2636 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2637 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2638 }
2640 // add the units menu
2641 {
2642 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2643 gtk_action_group_add_action( mainActions, act );
2644 }
2646 /* Reset */
2647 {
2648 InkAction* inky = ink_action_new( "RectResetAction",
2649 _("Not rounded"),
2650 _("Make corners sharp"),
2651 "squared_corner",
2652 secondarySize );
2653 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2654 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2655 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2656 g_object_set_data( holder, "not_rounded", inky );
2657 }
2659 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2660 sp_rtb_sensitivize( holder );
2662 sigc::connection *connection = new sigc::connection(
2663 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2664 );
2665 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2666 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2667 }
2669 //########################
2670 //## 3D Box ##
2671 //########################
2673 // normalize angle so that it lies in the interval [0,360]
2674 static double box3d_normalize_angle (double a) {
2675 double angle = a + ((int) (a/360.0))*360;
2676 if (angle < 0) {
2677 angle += 360.0;
2678 }
2679 return angle;
2680 }
2682 static void
2683 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2684 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2685 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2686 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2687 // are reset).
2688 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2690 if (is_infinite) {
2691 gtk_toggle_action_set_active(tact, TRUE);
2692 gtk_action_set_sensitive(act, TRUE);
2694 double angle = persp3d_get_infinite_angle(persp, axis);
2695 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2696 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2697 }
2698 } else {
2699 gtk_toggle_action_set_active(tact, FALSE);
2700 gtk_action_set_sensitive(act, FALSE);
2701 }
2702 }
2704 static void
2705 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2706 if (!persp_repr) {
2707 g_print ("No perspective given to box3d_resync_toolbar().\n");
2708 return;
2709 }
2711 GtkWidget *tbl = GTK_WIDGET(data);
2712 GtkAdjustment *adj = 0;
2713 GtkAction *act = 0;
2714 GtkToggleAction *tact = 0;
2715 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2716 {
2717 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2718 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2719 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2721 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2722 }
2723 {
2724 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2725 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2726 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2728 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2729 }
2730 {
2731 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2732 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2733 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2735 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2736 }
2737 }
2739 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2740 gchar const */*old_value*/, gchar const */*new_value*/,
2741 bool /*is_interactive*/, gpointer data)
2742 {
2743 GtkWidget *tbl = GTK_WIDGET(data);
2745 // quit if run by the attr_changed listener
2746 // note: it used to work without the differently called freeze_ attributes (here and in
2747 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2748 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2749 return;
2750 }
2752 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2753 // sp_document_maybe_done() when the document is undo insensitive)
2754 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2756 // TODO: Only update the appropriate part of the toolbar
2757 // if (!strcmp(name, "inkscape:vp_z")) {
2758 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2759 // }
2761 Persp3D *persp = persp3d_get_from_repr(repr);
2762 persp3d_update_box_reprs(persp);
2764 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2765 }
2767 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2768 {
2769 NULL, /* child_added */
2770 NULL, /* child_removed */
2771 box3d_persp_tb_event_attr_changed,
2772 NULL, /* content_changed */
2773 NULL /* order_changed */
2774 };
2776 /**
2777 * \param selection Should not be NULL.
2778 */
2779 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2780 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2781 static void
2782 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2783 {
2784 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2785 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2786 // update the perspectives with infinite VPs and leave the other ones untouched).
2788 Inkscape::XML::Node *persp_repr = NULL;
2789 purge_repr_listener(tbl, tbl);
2791 SPItem *item = selection->singleItem();
2792 if (item && SP_IS_BOX3D(item)) {
2793 // FIXME: Also deal with multiple selected boxes
2794 SPBox3D *box = SP_BOX3D(item);
2795 Persp3D *persp = box3d_get_perspective(box);
2796 persp_repr = SP_OBJECT_REPR(persp);
2797 if (persp_repr) {
2798 g_object_set_data(tbl, "repr", persp_repr);
2799 Inkscape::GC::anchor(persp_repr);
2800 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2801 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2802 }
2804 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2805 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2807 box3d_resync_toolbar(persp_repr, tbl);
2808 }
2809 }
2811 static void
2812 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2813 {
2814 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2815 SPDocument *document = sp_desktop_document(desktop);
2817 // quit if run by the attr_changed listener
2818 // note: it used to work without the differently called freeze_ attributes (here and in
2819 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2820 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2821 return;
2822 }
2824 // in turn, prevent listener from responding
2825 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2827 //Persp3D *persp = document->current_persp3d;
2828 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2829 if (sel_persps.empty()) {
2830 // this can happen when the document is created; we silently ignore it
2831 return;
2832 }
2833 Persp3D *persp = sel_persps.front();
2835 persp->tmat.set_infinite_direction (axis, adj->value);
2836 SP_OBJECT(persp)->updateRepr();
2838 // TODO: use the correct axis here, too
2839 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2841 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2842 }
2845 static void
2846 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2847 {
2848 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2849 }
2851 static void
2852 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2853 {
2854 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2855 }
2857 static void
2858 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2859 {
2860 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2861 }
2864 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2865 {
2866 // TODO: Take all selected perspectives into account
2867 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2868 if (sel_persps.empty()) {
2869 // this can happen when the document is created; we silently ignore it
2870 return;
2871 }
2872 Persp3D *persp = sel_persps.front();
2874 bool set_infinite = gtk_toggle_action_get_active(act);
2875 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2876 }
2878 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2879 {
2880 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2881 }
2883 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2884 {
2885 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2886 }
2888 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2889 {
2890 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2891 }
2893 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2894 {
2895 EgeAdjustmentAction* eact = 0;
2896 SPDocument *document = sp_desktop_document (desktop);
2897 Persp3D *persp = document->current_persp3d;
2899 EgeAdjustmentAction* box3d_angle_x = 0;
2900 EgeAdjustmentAction* box3d_angle_y = 0;
2901 EgeAdjustmentAction* box3d_angle_z = 0;
2903 /* Angle X */
2904 {
2905 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2906 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2907 eact = create_adjustment_action( "3DBoxAngleXAction",
2908 _("Angle in X direction"), _("Angle X:"),
2909 // Translators: PL is short for 'perspective line'
2910 _("Angle of PLs in X direction"),
2911 "tools.shapes.3dbox", "box3d_angle_x", 30,
2912 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2913 -360.0, 360.0, 1.0, 10.0,
2914 labels, values, G_N_ELEMENTS(labels),
2915 box3d_angle_x_value_changed );
2916 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2917 g_object_set_data( holder, "box3d_angle_x_action", eact );
2918 box3d_angle_x = eact;
2919 }
2921 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2922 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2923 } else {
2924 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2925 }
2928 /* VP X state */
2929 {
2930 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2931 // Translators: VP is short for 'vanishing point'
2932 _("State of VP in X direction"),
2933 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2934 "toggle_vp_x",
2935 Inkscape::ICON_SIZE_DECORATION );
2936 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2937 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2938 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2939 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2940 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2941 }
2943 /* Angle Y */
2944 {
2945 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2946 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2947 eact = create_adjustment_action( "3DBoxAngleYAction",
2948 _("Angle in Y direction"), _("Angle Y:"),
2949 // Translators: PL is short for 'perspective line'
2950 _("Angle of PLs in Y direction"),
2951 "tools.shapes.3dbox", "box3d_angle_y", 30,
2952 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2953 -360.0, 360.0, 1.0, 10.0,
2954 labels, values, G_N_ELEMENTS(labels),
2955 box3d_angle_y_value_changed );
2956 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2957 g_object_set_data( holder, "box3d_angle_y_action", eact );
2958 box3d_angle_y = eact;
2959 }
2961 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2962 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2963 } else {
2964 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2965 }
2967 /* VP Y state */
2968 {
2969 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2970 // Translators: VP is short for 'vanishing point'
2971 _("State of VP in Y direction"),
2972 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2973 "toggle_vp_y",
2974 Inkscape::ICON_SIZE_DECORATION );
2975 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2976 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2977 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2978 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2979 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2980 }
2982 /* Angle Z */
2983 {
2984 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2985 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2986 eact = create_adjustment_action( "3DBoxAngleZAction",
2987 _("Angle in Z direction"), _("Angle Z:"),
2988 // Translators: PL is short for 'perspective line'
2989 _("Angle of PLs in Z direction"),
2990 "tools.shapes.3dbox", "box3d_angle_z", 30,
2991 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2992 -360.0, 360.0, 1.0, 10.0,
2993 labels, values, G_N_ELEMENTS(labels),
2994 box3d_angle_z_value_changed );
2995 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2996 g_object_set_data( holder, "box3d_angle_z_action", eact );
2997 box3d_angle_z = eact;
2998 }
3000 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3001 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3002 } else {
3003 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3004 }
3006 /* VP Z state */
3007 {
3008 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3009 // Translators: VP is short for 'vanishing point'
3010 _("State of VP in Z direction"),
3011 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3012 "toggle_vp_z",
3013 Inkscape::ICON_SIZE_DECORATION );
3014 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3015 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3016 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3017 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3018 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3019 }
3021 sigc::connection *connection = new sigc::connection(
3022 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3023 );
3024 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3025 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3026 }
3028 //########################
3029 //## Spiral ##
3030 //########################
3032 static void
3033 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3034 {
3035 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3037 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3038 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3039 }
3041 // quit if run by the attr_changed listener
3042 if (g_object_get_data( tbl, "freeze" )) {
3043 return;
3044 }
3046 // in turn, prevent listener from responding
3047 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3049 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3051 bool modmade = false;
3052 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3053 items != NULL;
3054 items = items->next)
3055 {
3056 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3057 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3058 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3059 SP_OBJECT((SPItem *) items->data)->updateRepr();
3060 modmade = true;
3061 }
3062 }
3064 g_free(namespaced_name);
3066 if (modmade) {
3067 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3068 _("Change spiral"));
3069 }
3071 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3072 }
3074 static void
3075 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3076 {
3077 sp_spl_tb_value_changed(adj, tbl, "revolution");
3078 }
3080 static void
3081 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3082 {
3083 sp_spl_tb_value_changed(adj, tbl, "expansion");
3084 }
3086 static void
3087 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3088 {
3089 sp_spl_tb_value_changed(adj, tbl, "t0");
3090 }
3092 static void
3093 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3094 {
3095 GtkWidget *tbl = GTK_WIDGET(obj);
3097 GtkAdjustment *adj;
3099 // fixme: make settable
3100 gdouble rev = 5;
3101 gdouble exp = 1.0;
3102 gdouble t0 = 0.0;
3104 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3105 gtk_adjustment_set_value(adj, rev);
3106 gtk_adjustment_value_changed(adj);
3108 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3109 gtk_adjustment_set_value(adj, exp);
3110 gtk_adjustment_value_changed(adj);
3112 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3113 gtk_adjustment_set_value(adj, t0);
3114 gtk_adjustment_value_changed(adj);
3116 spinbutton_defocus(GTK_OBJECT(tbl));
3117 }
3120 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3121 gchar const */*old_value*/, gchar const */*new_value*/,
3122 bool /*is_interactive*/, gpointer data)
3123 {
3124 GtkWidget *tbl = GTK_WIDGET(data);
3126 // quit if run by the _changed callbacks
3127 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3128 return;
3129 }
3131 // in turn, prevent callbacks from responding
3132 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3134 GtkAdjustment *adj;
3135 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3136 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3138 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3139 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3141 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3142 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3144 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3145 }
3148 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3149 NULL, /* child_added */
3150 NULL, /* child_removed */
3151 spiral_tb_event_attr_changed,
3152 NULL, /* content_changed */
3153 NULL /* order_changed */
3154 };
3156 static void
3157 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3158 {
3159 int n_selected = 0;
3160 Inkscape::XML::Node *repr = NULL;
3162 purge_repr_listener( tbl, tbl );
3164 for (GSList const *items = selection->itemList();
3165 items != NULL;
3166 items = items->next)
3167 {
3168 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3169 n_selected++;
3170 repr = SP_OBJECT_REPR((SPItem *) items->data);
3171 }
3172 }
3174 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3176 if (n_selected == 0) {
3177 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3178 } else if (n_selected == 1) {
3179 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3181 if (repr) {
3182 g_object_set_data( tbl, "repr", repr );
3183 Inkscape::GC::anchor(repr);
3184 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3185 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3186 }
3187 } else {
3188 // FIXME: implement averaging of all parameters for multiple selected
3189 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3190 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3191 }
3192 }
3195 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3196 {
3197 EgeAdjustmentAction* eact = 0;
3198 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3200 {
3201 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3202 ege_output_action_set_use_markup( act, TRUE );
3203 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3204 g_object_set_data( holder, "mode_action", act );
3205 }
3207 /* Revolution */
3208 {
3209 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3210 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3211 eact = create_adjustment_action( "SpiralRevolutionAction",
3212 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3213 "tools.shapes.spiral", "revolution", 3.0,
3214 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3215 0.01, 1024.0, 0.1, 1.0,
3216 labels, values, G_N_ELEMENTS(labels),
3217 sp_spl_tb_revolution_value_changed, 1, 2);
3218 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3219 }
3221 /* Expansion */
3222 {
3223 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3224 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3225 eact = create_adjustment_action( "SpiralExpansionAction",
3226 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3227 "tools.shapes.spiral", "expansion", 1.0,
3228 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3229 0.0, 1000.0, 0.01, 1.0,
3230 labels, values, G_N_ELEMENTS(labels),
3231 sp_spl_tb_expansion_value_changed);
3232 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3233 }
3235 /* T0 */
3236 {
3237 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3238 gdouble values[] = {0, 0.5, 0.9};
3239 eact = create_adjustment_action( "SpiralT0Action",
3240 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3241 "tools.shapes.spiral", "t0", 0.0,
3242 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3243 0.0, 0.999, 0.01, 1.0,
3244 labels, values, G_N_ELEMENTS(labels),
3245 sp_spl_tb_t0_value_changed);
3246 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3247 }
3249 /* Reset */
3250 {
3251 InkAction* inky = ink_action_new( "SpiralResetAction",
3252 _("Defaults"),
3253 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3254 GTK_STOCK_CLEAR,
3255 secondarySize );
3256 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3257 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3258 }
3261 sigc::connection *connection = new sigc::connection(
3262 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3263 );
3264 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3265 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3266 }
3268 //########################
3269 //## Pen/Pencil ##
3270 //########################
3272 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3273 {
3274 prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3275 }
3277 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3278 {
3279 // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3280 // adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3281 {
3282 EgeOutputAction* act = ege_output_action_new(
3283 tool_is_pencil ?
3284 "FreehandModeActionPencilTemp" :
3285 "FreehandModeActionPenTemp",
3286 _("<b>Mode:</b>"), "", 0 );
3287 ege_output_action_set_use_markup( act, TRUE );
3288 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3289 g_object_set_data( holder, "freehand_mode_action", act );
3290 }
3292 /* Freehand mode toggle buttons */
3293 {
3294 //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3295 //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3296 guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3297 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3299 {
3300 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3302 GtkTreeIter iter;
3303 gtk_list_store_append( model, &iter );
3304 gtk_list_store_set( model, &iter,
3305 0, _("Bézier"),
3306 1, _("Regular Bézier mode"),
3307 2, "bezier_mode",
3308 -1 );
3310 gtk_list_store_append( model, &iter );
3311 gtk_list_store_set( model, &iter,
3312 0, _("Spiro"),
3313 1, _("Spiro splines mode"),
3314 2, "spiro_splines_mode",
3315 -1 );
3317 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3318 "FreehandModeActionPencil" :
3319 "FreehandModeActionPen",
3320 (""), (""), NULL, GTK_TREE_MODEL(model) );
3321 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3322 g_object_set_data( holder, "freehande_mode_action", act );
3324 ege_select_one_action_set_appearance( act, "full" );
3325 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3326 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3327 ege_select_one_action_set_icon_column( act, 2 );
3328 ege_select_one_action_set_icon_size( act, secondarySize );
3329 ege_select_one_action_set_tooltip_column( act, 1 );
3331 ege_select_one_action_set_active( act, spiroMode);
3332 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3333 }
3334 }
3335 }
3337 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject */*dataKludge*/) {
3338 gint shape = ege_select_one_action_get_active( act );
3339 prefs_set_int_attribute("tools.freehand", "shape", shape);
3340 }
3342 /**
3343 * \brief Generate the list of freehand advanced shape option entries.
3344 */
3345 GList * freehand_shape_dropdown_items_list() {
3346 GList *glist = NULL;
3348 glist = g_list_append (glist, _("None"));
3349 glist = g_list_append (glist, _("Clipboard"));
3350 glist = g_list_append (glist, _("Crescendo"));
3351 glist = g_list_append (glist, _("Decrescendo"));
3353 return glist;
3354 }
3356 static void
3357 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3358 /*advanced shape options */
3359 {
3360 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3362 GList* items = 0;
3363 gint count = 0;
3364 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3365 {
3366 GtkTreeIter iter;
3367 gtk_list_store_append( model, &iter );
3368 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3369 count++;
3370 }
3371 g_list_free( items );
3372 items = 0;
3373 EgeSelectOneAction* act1 = ege_select_one_action_new(
3374 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3375 _("Shape:"), (""), NULL, GTK_TREE_MODEL(model));
3376 g_object_set( act1, "short_label", _("Shape:"), NULL );
3377 ege_select_one_action_set_appearance( act1, "compact" );
3378 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.freehand", "shape", 0) );
3379 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3380 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3381 g_object_set_data( holder, "shape_action", act1 );
3382 }
3383 }
3385 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3386 {
3387 sp_add_spiro_toggle(mainActions, holder, false);
3388 sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3389 }
3392 static void
3393 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3394 {
3395 GtkWidget *tbl = GTK_WIDGET(obj);
3397 GtkAdjustment *adj;
3399 // fixme: make settable
3400 gdouble tolerance = 4;
3402 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3403 gtk_adjustment_set_value(adj, tolerance);
3404 gtk_adjustment_value_changed(adj);
3406 spinbutton_defocus(GTK_OBJECT(tbl));
3407 }
3409 static void
3410 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3411 {
3413 // quit if run by the attr_changed listener
3414 if (g_object_get_data( tbl, "freeze" )) {
3415 return;
3416 }
3417 // in turn, prevent listener from responding
3418 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3419 prefs_set_double_attribute("tools.freehand.pencil",
3420 "tolerance", adj->value);
3421 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3423 }
3427 static void
3428 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3429 const gchar */*key*/,
3430 const gchar */*oldval*/,
3431 const gchar */*newval*/,
3432 bool /*is_interactive*/,
3433 void * data)
3434 {
3435 GObject* tbl = G_OBJECT(data);
3436 if (g_object_get_data( tbl, "freeze" )) {
3437 return;
3438 }
3440 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3442 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3443 "tolerance");
3445 double v = prefs_get_double_attribute("tools.freehand.pencil",
3446 "tolerance", adj->value);
3447 gtk_adjustment_set_value(adj, v);
3448 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3450 }
3452 static Inkscape::XML::NodeEventVector pencil_node_events =
3453 {
3454 NULL,
3455 NULL,
3456 sp_pencil_tb_tolerance_value_changed_external,
3457 NULL,
3458 NULL,
3459 };
3462 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3463 {
3464 sp_add_spiro_toggle(mainActions, holder, true);
3466 EgeAdjustmentAction* eact = 0;
3468 /* Tolerance */
3469 {
3471 eact = create_adjustment_action( "PencilToleranceAction",
3472 _("Number of pixels allowed in interpolating"),
3473 _("Tolerance:"), _("Tolerance"),
3474 "tools.freehand.pencil", "tolerance",
3475 3.0,
3476 GTK_WIDGET(desktop->canvas), NULL,
3477 holder, TRUE, "altx-pencil",
3478 0.5, 100.0, 0.5, 0,
3479 NULL, NULL, 0,
3480 sp_pencil_tb_tolerance_value_changed,
3481 1, 2);
3482 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3483 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3485 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3486 "tools.freehand.pencil");
3487 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3488 g_object_set_data(G_OBJECT(holder), "repr", repr);
3490 }
3492 /* advanced shape options */
3493 sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3495 /* Reset */
3496 {
3497 InkAction* inky = ink_action_new( "PencilResetAction",
3498 _("Defaults"),
3499 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3500 GTK_STOCK_CLEAR,
3501 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3502 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3503 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3504 }
3506 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3508 }
3511 //########################
3512 //## Tweak ##
3513 //########################
3515 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3516 {
3517 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3518 }
3520 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3521 {
3522 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3523 }
3525 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3526 {
3527 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3528 }
3530 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3531 {
3532 int mode = ege_select_one_action_get_active( act );
3533 prefs_set_int_attribute("tools.tweak", "mode", mode);
3535 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3536 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3537 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3538 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3539 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3540 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3541 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3542 if (doh) gtk_action_set_sensitive (doh, TRUE);
3543 if (dos) gtk_action_set_sensitive (dos, TRUE);
3544 if (dol) gtk_action_set_sensitive (dol, TRUE);
3545 if (doo) gtk_action_set_sensitive (doo, TRUE);
3546 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3547 if (fid) gtk_action_set_sensitive (fid, FALSE);
3548 } else {
3549 if (doh) gtk_action_set_sensitive (doh, FALSE);
3550 if (dos) gtk_action_set_sensitive (dos, FALSE);
3551 if (dol) gtk_action_set_sensitive (dol, FALSE);
3552 if (doo) gtk_action_set_sensitive (doo, FALSE);
3553 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3554 if (fid) gtk_action_set_sensitive (fid, TRUE);
3555 }
3556 }
3558 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3559 {
3560 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3561 }
3563 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3564 bool show = gtk_toggle_action_get_active( act );
3565 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3566 }
3567 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3568 bool show = gtk_toggle_action_get_active( act );
3569 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3570 }
3571 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3572 bool show = gtk_toggle_action_get_active( act );
3573 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3574 }
3575 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3576 bool show = gtk_toggle_action_get_active( act );
3577 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3578 }
3580 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3581 {
3582 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3584 {
3585 /* Width */
3586 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3587 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3588 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3589 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3590 "tools.tweak", "width", 15,
3591 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3592 1, 100, 1.0, 0.0,
3593 labels, values, G_N_ELEMENTS(labels),
3594 sp_tweak_width_value_changed, 0.01, 0, 100 );
3595 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3596 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3597 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3598 }
3601 {
3602 /* Force */
3603 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3604 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3605 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3606 _("Force"), _("Force:"), _("The force of the tweak action"),
3607 "tools.tweak", "force", 20,
3608 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3609 1, 100, 1.0, 0.0,
3610 labels, values, G_N_ELEMENTS(labels),
3611 sp_tweak_force_value_changed, 0.01, 0, 100 );
3612 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3613 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3614 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3615 }
3617 /* Mode */
3618 {
3619 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3621 GtkTreeIter iter;
3622 gtk_list_store_append( model, &iter );
3623 gtk_list_store_set( model, &iter,
3624 0, _("Push mode"),
3625 1, _("Push parts of paths in any direction"),
3626 2, "tweak_push_mode",
3627 -1 );
3629 gtk_list_store_append( model, &iter );
3630 gtk_list_store_set( model, &iter,
3631 0, _("Shrink mode"),
3632 1, _("Shrink (inset) parts of paths"),
3633 2, "tweak_shrink_mode",
3634 -1 );
3636 gtk_list_store_append( model, &iter );
3637 gtk_list_store_set( model, &iter,
3638 0, _("Grow mode"),
3639 1, _("Grow (outset) parts of paths"),
3640 2, "tweak_grow_mode",
3641 -1 );
3643 gtk_list_store_append( model, &iter );
3644 gtk_list_store_set( model, &iter,
3645 0, _("Attract mode"),
3646 1, _("Attract parts of paths towards cursor"),
3647 2, "tweak_attract_mode",
3648 -1 );
3650 gtk_list_store_append( model, &iter );
3651 gtk_list_store_set( model, &iter,
3652 0, _("Repel mode"),
3653 1, _("Repel parts of paths from cursor"),
3654 2, "tweak_repel_mode",
3655 -1 );
3657 gtk_list_store_append( model, &iter );
3658 gtk_list_store_set( model, &iter,
3659 0, _("Roughen mode"),
3660 1, _("Roughen parts of paths"),
3661 2, "tweak_roughen_mode",
3662 -1 );
3664 gtk_list_store_append( model, &iter );
3665 gtk_list_store_set( model, &iter,
3666 0, _("Color paint mode"),
3667 1, _("Paint the tool's color upon selected objects"),
3668 2, "tweak_colorpaint_mode",
3669 -1 );
3671 gtk_list_store_append( model, &iter );
3672 gtk_list_store_set( model, &iter,
3673 0, _("Color jitter mode"),
3674 1, _("Jitter the colors of selected objects"),
3675 2, "tweak_colorjitter_mode",
3676 -1 );
3678 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3679 g_object_set( act, "short_label", _("Mode:"), NULL );
3680 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3681 g_object_set_data( holder, "mode_action", act );
3683 ege_select_one_action_set_appearance( act, "full" );
3684 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3685 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3686 ege_select_one_action_set_icon_column( act, 2 );
3687 ege_select_one_action_set_icon_size( act, secondarySize );
3688 ege_select_one_action_set_tooltip_column( act, 1 );
3690 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3691 ege_select_one_action_set_active( act, mode );
3692 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3694 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3695 }
3697 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3699 {
3700 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3701 ege_output_action_set_use_markup( act, TRUE );
3702 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3703 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3704 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3705 g_object_set_data( holder, "tweak_channels_label", act);
3706 }
3708 {
3709 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3710 _("Hue"),
3711 _("In color mode, act on objects' hue"),
3712 NULL,
3713 Inkscape::ICON_SIZE_DECORATION );
3714 //TRANSLATORS: "H" here stands for hue
3715 g_object_set( act, "short_label", _("H"), NULL );
3716 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3717 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3718 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3719 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3720 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3721 g_object_set_data( holder, "tweak_doh", act);
3722 }
3723 {
3724 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3725 _("Saturation"),
3726 _("In color mode, act on objects' saturation"),
3727 NULL,
3728 Inkscape::ICON_SIZE_DECORATION );
3729 //TRANSLATORS: "S" here stands for Saturation
3730 g_object_set( act, "short_label", _("S"), NULL );
3731 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3732 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3733 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3734 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3735 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3736 g_object_set_data( holder, "tweak_dos", act );
3737 }
3738 {
3739 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3740 _("Lightness"),
3741 _("In color mode, act on objects' lightness"),
3742 NULL,
3743 Inkscape::ICON_SIZE_DECORATION );
3744 //TRANSLATORS: "L" here stands for Lightness
3745 g_object_set( act, "short_label", _("L"), NULL );
3746 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3747 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3748 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3749 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3750 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3751 g_object_set_data( holder, "tweak_dol", act );
3752 }
3753 {
3754 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3755 _("Opacity"),
3756 _("In color mode, act on objects' opacity"),
3757 NULL,
3758 Inkscape::ICON_SIZE_DECORATION );
3759 //TRANSLATORS: "O" here stands for Opacity
3760 g_object_set( act, "short_label", _("O"), NULL );
3761 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3762 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3763 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3764 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3765 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3766 g_object_set_data( holder, "tweak_doo", act );
3767 }
3769 { /* Fidelity */
3770 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3771 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3772 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3773 _("Fidelity"), _("Fidelity:"),
3774 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3775 "tools.tweak", "fidelity", 50,
3776 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3777 1, 100, 1.0, 10.0,
3778 labels, values, G_N_ELEMENTS(labels),
3779 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3780 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3781 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3782 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3783 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3784 g_object_set_data( holder, "tweak_fidelity", eact );
3785 }
3788 /* Use Pressure button */
3789 {
3790 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3791 _("Pressure"),
3792 _("Use the pressure of the input device to alter the force of tweak action"),
3793 "use_pressure",
3794 Inkscape::ICON_SIZE_DECORATION );
3795 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3796 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3797 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3798 }
3800 }
3803 //########################
3804 //## Calligraphy ##
3805 //########################
3806 static void update_presets_list(GObject *dataKludge ){
3807 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3808 if (sel) {
3809 ege_select_one_action_set_active(sel, 0 );
3810 }
3811 }
3813 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3814 {
3815 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3816 update_presets_list(tbl);
3817 }
3819 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3820 {
3821 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3822 update_presets_list(tbl);
3823 }
3825 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3826 {
3827 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3828 update_presets_list(tbl);
3829 }
3831 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3832 {
3833 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3834 update_presets_list(tbl);
3835 }
3837 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3838 {
3839 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3840 update_presets_list(tbl);
3841 }
3843 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3844 {
3845 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3846 update_presets_list(tbl);
3847 }
3849 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3850 {
3851 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3852 update_presets_list(tbl);
3853 }
3855 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3856 {
3857 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3858 update_presets_list(tbl);
3859 }
3861 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3862 {
3863 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3864 update_presets_list(tbl);
3865 }
3867 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3868 {
3869 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3870 update_presets_list(tbl);
3871 }
3873 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3874 {
3875 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3876 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3877 update_presets_list(tbl);
3878 if (calligraphy_angle )
3879 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3880 }
3883 #define PROFILE_FLOAT_SIZE 7
3884 #define PROFILE_INT_SIZE 4
3885 struct ProfileFloatElement {
3886 char const *name;
3887 double def;
3888 double min;
3889 double max;
3890 };
3891 struct ProfileIntElement {
3892 char const *name;
3893 int def;
3894 int min;
3895 int max;
3896 };
3900 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3901 {"mass",2, 0.0, 100},
3902 {"wiggle",0.0, 0.0, 100},
3903 {"angle",30.0, -90.0, 90.0},
3904 {"thinning",10, -100, 100},
3905 {"tremor",0.0, 0.0, 100},
3906 {"flatness",90, 0.0, 100},
3907 {"cap_rounding",0.0, 0.0, 5.0}
3908 };
3909 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3910 {"width",15, 1, 100},
3911 {"usepressure",1,0,1},
3912 {"tracebackground",0,0,1},
3913 {"usetilt",1,0,1},
3914 };
3918 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3919 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3920 if (! desktop) return;
3922 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3923 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3924 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3926 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3927 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3928 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3930 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3931 ProfileFloatElement const &pe = f_profile[i];
3932 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3933 prefs_set_double_attribute(pref_path,pe.name,v);
3934 }
3935 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3936 ProfileIntElement const &pe = i_profile[i];
3937 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3938 prefs_set_int_attribute(pref_path,pe.name,v);
3939 }
3940 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3942 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3943 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3944 GtkTreeIter iter;
3945 gtk_list_store_append( model, &iter );
3946 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3948 free(profile_id);
3949 free(pref_path);
3951 ege_select_one_action_set_active(selector, new_index);
3952 }
3955 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3957 gint preset_index = ege_select_one_action_get_active( act );
3958 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3960 if ( profile_name) {
3961 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3962 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3963 ProfileFloatElement const &pe = f_profile[i];
3964 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3965 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3966 if ( adj ) {
3967 gtk_adjustment_set_value(adj, v);
3968 }
3969 }
3970 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3971 ProfileIntElement const &pe = i_profile[i];
3972 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3973 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3974 if ( toggle ) {
3975 gtk_toggle_action_set_active(toggle, v);
3976 } else printf("No toggle");
3977 }
3978 free(profile_name);
3979 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3980 }
3982 }
3985 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3986 {
3987 {
3988 EgeAdjustmentAction* calligraphy_angle = 0;
3990 {
3991 /* Width */
3992 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3993 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3994 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3995 _("Pen Width"), _("Width:"),
3996 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3997 "tools.calligraphic", "width", 15,
3998 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3999 1, 100, 1.0, 0.0,
4000 labels, values, G_N_ELEMENTS(labels),
4001 sp_ddc_width_value_changed, 0.01, 0, 100 );
4002 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4003 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4004 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4005 }
4007 {
4008 /* Thinning */
4009 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4010 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4011 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4012 _("Stroke Thinning"), _("Thinning:"),
4013 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4014 "tools.calligraphic", "thinning", 10,
4015 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4016 -100, 100, 1, 0.1,
4017 labels, values, G_N_ELEMENTS(labels),
4018 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4019 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4020 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4021 }
4023 {
4024 /* Angle */
4025 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4026 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4027 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4028 _("Pen Angle"), _("Angle:"),
4029 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4030 "tools.calligraphic", "angle", 30,
4031 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4032 -90.0, 90.0, 1.0, 10.0,
4033 labels, values, G_N_ELEMENTS(labels),
4034 sp_ddc_angle_value_changed, 1, 0 );
4035 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4036 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4037 g_object_set_data( holder, "angle", eact );
4038 calligraphy_angle = eact;
4039 }
4041 {
4042 /* Fixation */
4043 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4044 gdouble values[] = {0, 20, 40, 60, 90, 100};
4045 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4046 _("Fixation"), _("Fixation:"),
4047 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4048 "tools.calligraphic", "flatness", 90,
4049 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4050 0.0, 100, 1.0, 10.0,
4051 labels, values, G_N_ELEMENTS(labels),
4052 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4053 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4054 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4055 }
4057 {
4058 /* Cap Rounding */
4059 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4060 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4061 // TRANSLATORS: "cap" means "end" (both start and finish) here
4062 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4063 _("Cap rounding"), _("Caps:"),
4064 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4065 "tools.calligraphic", "cap_rounding", 0.0,
4066 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4067 0.0, 5.0, 0.01, 0.1,
4068 labels, values, G_N_ELEMENTS(labels),
4069 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4070 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4071 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4072 }
4074 {
4075 /* Tremor */
4076 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4077 gdouble values[] = {0, 10, 20, 40, 60, 100};
4078 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4079 _("Stroke Tremor"), _("Tremor:"),
4080 _("Increase to make strokes rugged and trembling"),
4081 "tools.calligraphic", "tremor", 0.0,
4082 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4083 0.0, 100, 1, 0.0,
4084 labels, values, G_N_ELEMENTS(labels),
4085 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4087 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4088 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4089 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4090 }
4092 {
4093 /* Wiggle */
4094 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4095 gdouble values[] = {0, 20, 40, 60, 100};
4096 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4097 _("Pen Wiggle"), _("Wiggle:"),
4098 _("Increase to make the pen waver and wiggle"),
4099 "tools.calligraphic", "wiggle", 0.0,
4100 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4101 0.0, 100, 1, 0.0,
4102 labels, values, G_N_ELEMENTS(labels),
4103 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4104 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4105 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4106 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4107 }
4109 {
4110 /* Mass */
4111 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4112 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4113 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4114 _("Pen Mass"), _("Mass:"),
4115 _("Increase to make the pen drag behind, as if slowed by inertia"),
4116 "tools.calligraphic", "mass", 2.0,
4117 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4118 0.0, 100, 1, 0.0,
4119 labels, values, G_N_ELEMENTS(labels),
4120 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4121 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4122 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4123 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4124 }
4127 /* Trace Background button */
4128 {
4129 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4130 _("Trace Background"),
4131 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4132 "trace_background",
4133 Inkscape::ICON_SIZE_DECORATION );
4134 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4135 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4136 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4137 g_object_set_data( holder, "tracebackground", act );
4138 }
4140 /* Use Pressure button */
4141 {
4142 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4143 _("Pressure"),
4144 _("Use the pressure of the input device to alter the width of the pen"),
4145 "use_pressure",
4146 Inkscape::ICON_SIZE_DECORATION );
4147 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4148 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4149 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4150 g_object_set_data( holder, "usepressure", act );
4151 }
4153 /* Use Tilt button */
4154 {
4155 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4156 _("Tilt"),
4157 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4158 "use_tilt",
4159 Inkscape::ICON_SIZE_DECORATION );
4160 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4161 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4162 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4163 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4164 g_object_set_data( holder, "usetilt", act );
4165 }
4167 /*calligraphic profile */
4168 {
4169 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4170 gchar *pref_path;
4173 GtkTreeIter iter;
4174 gtk_list_store_append( model, &iter );
4175 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4177 //TODO: switch back to prefs API
4178 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4179 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4180 int ii=1;
4181 while (child_repr) {
4182 GtkTreeIter iter;
4183 char *preset_name = (char *) child_repr->attribute("name");
4184 gtk_list_store_append( model, &iter );
4185 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4186 child_repr = sp_repr_next(child_repr);
4187 }
4189 pref_path = NULL;
4190 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4191 ege_select_one_action_set_appearance( act1, "compact" );
4192 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4193 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4194 g_object_set_data( holder, "profile_selector", act1 );
4196 }
4198 /*Save or delete calligraphic profile */
4199 {
4200 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4201 _("Defaults"),
4202 _("Save current settings as new profile"),
4203 GTK_STOCK_SAVE );
4204 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4207 gtk_action_group_add_action( mainActions, act );
4208 gtk_action_set_sensitive( act, TRUE );
4209 g_object_set_data( holder, "profile_save_delete", act );
4210 }
4211 }
4212 }
4215 //########################
4216 //## Circle / Arc ##
4217 //########################
4219 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4220 {
4221 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4222 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4224 if (v1 == 0 && v2 == 0) {
4225 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4226 gtk_action_set_sensitive( ocb, FALSE );
4227 gtk_action_set_sensitive( make_whole, FALSE );
4228 }
4229 } else {
4230 gtk_action_set_sensitive( ocb, TRUE );
4231 gtk_action_set_sensitive( make_whole, TRUE );
4232 }
4233 }
4235 static void
4236 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4237 {
4238 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4240 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4241 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4242 }
4244 // quit if run by the attr_changed listener
4245 if (g_object_get_data( tbl, "freeze" )) {
4246 return;
4247 }
4249 // in turn, prevent listener from responding
4250 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4252 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4254 bool modmade = false;
4255 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4256 items != NULL;
4257 items = items->next)
4258 {
4259 SPItem *item = SP_ITEM(items->data);
4261 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4263 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4264 SPArc *arc = SP_ARC(item);
4266 if (!strcmp(value_name, "start"))
4267 ge->start = (adj->value * M_PI)/ 180;
4268 else
4269 ge->end = (adj->value * M_PI)/ 180;
4271 sp_genericellipse_normalize(ge);
4272 ((SPObject *)arc)->updateRepr();
4273 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4275 modmade = true;
4276 }
4277 }
4279 g_free(namespaced_name);
4281 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4283 sp_arctb_sensitivize( tbl, adj->value, other->value );
4285 if (modmade) {
4286 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4287 _("Arc: Change start/end"));
4288 }
4290 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4291 }
4294 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4295 {
4296 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4297 }
4299 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4300 {
4301 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4302 }
4305 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4306 {
4307 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4308 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4309 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4310 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4311 }
4313 // only take action if run by the attr_changed listener
4314 if (!g_object_get_data( tbl, "freeze" )) {
4315 // in turn, prevent listener from responding
4316 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4318 if ( eraserMode != 0 ) {
4319 } else {
4320 }
4321 // TODO finish implementation
4323 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4324 }
4325 }
4327 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4328 {
4329 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4330 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4331 if ( ege_select_one_action_get_active( act ) != 0 ) {
4332 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4333 } else {
4334 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4335 }
4336 }
4338 // quit if run by the attr_changed listener
4339 if (g_object_get_data( tbl, "freeze" )) {
4340 return;
4341 }
4343 // in turn, prevent listener from responding
4344 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4346 bool modmade = false;
4348 if ( ege_select_one_action_get_active(act) != 0 ) {
4349 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4350 items != NULL;
4351 items = items->next)
4352 {
4353 if (SP_IS_ARC((SPItem *) items->data)) {
4354 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4355 repr->setAttribute("sodipodi:open", "true");
4356 SP_OBJECT((SPItem *) items->data)->updateRepr();
4357 modmade = true;
4358 }
4359 }
4360 } else {
4361 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4362 items != NULL;
4363 items = items->next)
4364 {
4365 if (SP_IS_ARC((SPItem *) items->data)) {
4366 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4367 repr->setAttribute("sodipodi:open", NULL);
4368 SP_OBJECT((SPItem *) items->data)->updateRepr();
4369 modmade = true;
4370 }
4371 }
4372 }
4374 if (modmade) {
4375 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4376 _("Arc: Change open/closed"));
4377 }
4379 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4380 }
4382 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4383 {
4384 GtkAdjustment *adj;
4385 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4386 gtk_adjustment_set_value(adj, 0.0);
4387 gtk_adjustment_value_changed(adj);
4389 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4390 gtk_adjustment_set_value(adj, 0.0);
4391 gtk_adjustment_value_changed(adj);
4393 spinbutton_defocus( GTK_OBJECT(obj) );
4394 }
4396 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4397 gchar const */*old_value*/, gchar const */*new_value*/,
4398 bool /*is_interactive*/, gpointer data)
4399 {
4400 GObject *tbl = G_OBJECT(data);
4402 // quit if run by the _changed callbacks
4403 if (g_object_get_data( tbl, "freeze" )) {
4404 return;
4405 }
4407 // in turn, prevent callbacks from responding
4408 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4410 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4411 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4413 GtkAdjustment *adj1,*adj2;
4414 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4415 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4416 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4417 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4419 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4421 char const *openstr = NULL;
4422 openstr = repr->attribute("sodipodi:open");
4423 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4425 if (openstr) {
4426 ege_select_one_action_set_active( ocb, 1 );
4427 } else {
4428 ege_select_one_action_set_active( ocb, 0 );
4429 }
4431 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4432 }
4434 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4435 NULL, /* child_added */
4436 NULL, /* child_removed */
4437 arc_tb_event_attr_changed,
4438 NULL, /* content_changed */
4439 NULL /* order_changed */
4440 };
4443 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4444 {
4445 int n_selected = 0;
4446 Inkscape::XML::Node *repr = NULL;
4448 purge_repr_listener( tbl, tbl );
4450 for (GSList const *items = selection->itemList();
4451 items != NULL;
4452 items = items->next)
4453 {
4454 if (SP_IS_ARC((SPItem *) items->data)) {
4455 n_selected++;
4456 repr = SP_OBJECT_REPR((SPItem *) items->data);
4457 }
4458 }
4460 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4462 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4463 if (n_selected == 0) {
4464 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4465 } else if (n_selected == 1) {
4466 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4467 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4469 if (repr) {
4470 g_object_set_data( tbl, "repr", repr );
4471 Inkscape::GC::anchor(repr);
4472 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4473 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4474 }
4475 } else {
4476 // FIXME: implement averaging of all parameters for multiple selected
4477 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4478 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4479 sp_arctb_sensitivize( tbl, 1, 0 );
4480 }
4481 }
4484 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4485 {
4486 EgeAdjustmentAction* eact = 0;
4487 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4490 {
4491 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4492 ege_output_action_set_use_markup( act, TRUE );
4493 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4494 g_object_set_data( holder, "mode_action", act );
4495 }
4497 /* Start */
4498 {
4499 eact = create_adjustment_action( "ArcStartAction",
4500 _("Start"), _("Start:"),
4501 _("The angle (in degrees) from the horizontal to the arc's start point"),
4502 "tools.shapes.arc", "start", 0.0,
4503 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4504 -360.0, 360.0, 1.0, 10.0,
4505 0, 0, 0,
4506 sp_arctb_start_value_changed);
4507 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4508 }
4510 /* End */
4511 {
4512 eact = create_adjustment_action( "ArcEndAction",
4513 _("End"), _("End:"),
4514 _("The angle (in degrees) from the horizontal to the arc's end point"),
4515 "tools.shapes.arc", "end", 0.0,
4516 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4517 -360.0, 360.0, 1.0, 10.0,
4518 0, 0, 0,
4519 sp_arctb_end_value_changed);
4520 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4521 }
4523 /* Segments / Pie checkbox */
4524 {
4525 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4527 GtkTreeIter iter;
4528 gtk_list_store_append( model, &iter );
4529 gtk_list_store_set( model, &iter,
4530 0, _("Closed arc"),
4531 1, _("Switch to segment (closed shape with two radii)"),
4532 2, "circle_closed_arc",
4533 -1 );
4535 gtk_list_store_append( model, &iter );
4536 gtk_list_store_set( model, &iter,
4537 0, _("Open Arc"),
4538 1, _("Switch to arc (unclosed shape)"),
4539 2, "circle_open_arc",
4540 -1 );
4542 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4543 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4544 g_object_set_data( holder, "open_action", act );
4546 ege_select_one_action_set_appearance( act, "full" );
4547 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4548 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4549 ege_select_one_action_set_icon_column( act, 2 );
4550 ege_select_one_action_set_icon_size( act, secondarySize );
4551 ege_select_one_action_set_tooltip_column( act, 1 );
4553 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4554 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4555 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4556 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4557 }
4559 /* Make Whole */
4560 {
4561 InkAction* inky = ink_action_new( "ArcResetAction",
4562 _("Make whole"),
4563 _("Make the shape a whole ellipse, not arc or segment"),
4564 "reset_circle",
4565 secondarySize );
4566 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4567 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4568 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4569 g_object_set_data( holder, "make_whole", inky );
4570 }
4572 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4573 // sensitivize make whole and open checkbox
4574 {
4575 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4576 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4577 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4578 }
4581 sigc::connection *connection = new sigc::connection(
4582 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4583 );
4584 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4585 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4586 }
4591 // toggle button callbacks and updaters
4593 //########################
4594 //## Dropper ##
4595 //########################
4597 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4598 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4599 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4600 if ( set_action ) {
4601 if ( gtk_toggle_action_get_active( act ) ) {
4602 gtk_action_set_sensitive( set_action, TRUE );
4603 } else {
4604 gtk_action_set_sensitive( set_action, FALSE );
4605 }
4606 }
4608 spinbutton_defocus(GTK_OBJECT(tbl));
4609 }
4611 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4612 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4613 spinbutton_defocus(GTK_OBJECT(tbl));
4614 }
4617 /**
4618 * Dropper auxiliary toolbar construction and setup.
4619 *
4620 * TODO: Would like to add swatch of current color.
4621 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4622 * can drag and drop places. Will provide a nice mixing palette.
4623 */
4624 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4625 {
4626 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4628 {
4629 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4630 ege_output_action_set_use_markup( act, TRUE );
4631 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4632 }
4634 {
4635 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4636 _("Pick opacity"),
4637 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4638 NULL,
4639 Inkscape::ICON_SIZE_DECORATION );
4640 g_object_set( act, "short_label", _("Pick"), NULL );
4641 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4642 g_object_set_data( holder, "pick_action", act );
4643 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4644 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4645 }
4647 {
4648 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4649 _("Assign opacity"),
4650 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4651 NULL,
4652 Inkscape::ICON_SIZE_DECORATION );
4653 g_object_set( act, "short_label", _("Assign"), NULL );
4654 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4655 g_object_set_data( holder, "set_action", act );
4656 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4657 // make sure it's disabled if we're not picking alpha
4658 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4659 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4660 }
4661 }
4665 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4666 {
4667 {
4668 /* Width */
4669 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4670 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4671 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4672 _("Pen Width"), _("Width:"),
4673 _("The width of the eraser pen (relative to the visible canvas area)"),
4674 "tools.eraser", "width", 15,
4675 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4676 1, 100, 1.0, 0.0,
4677 labels, values, G_N_ELEMENTS(labels),
4678 sp_ddc_width_value_changed, 0.01, 0, 100 );
4679 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4680 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4681 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4682 }
4684 {
4685 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4687 GtkTreeIter iter;
4688 gtk_list_store_append( model, &iter );
4689 gtk_list_store_set( model, &iter,
4690 0, _("Delete"),
4691 1, _("Delete objects touched by the eraser"),
4692 2, "delete_object",
4693 -1 );
4695 gtk_list_store_append( model, &iter );
4696 gtk_list_store_set( model, &iter,
4697 0, _("Cut"),
4698 1, _("Cut out from objects"),
4699 2, "difference",
4700 -1 );
4702 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4703 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4704 g_object_set_data( holder, "eraser_mode_action", act );
4706 ege_select_one_action_set_appearance( act, "full" );
4707 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4708 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4709 ege_select_one_action_set_icon_column( act, 2 );
4710 ege_select_one_action_set_tooltip_column( act, 1 );
4712 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4713 ege_select_one_action_set_active( act, eraserMode );
4714 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4715 }
4717 }
4719 //########################
4720 //## Text Toolbox ##
4721 //########################
4722 /*
4723 static void
4724 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4725 {
4726 //Call back for letter sizing spinbutton
4727 }
4729 static void
4730 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4731 {
4732 //Call back for line height spinbutton
4733 }
4735 static void
4736 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4737 {
4738 //Call back for horizontal kerning spinbutton
4739 }
4741 static void
4742 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4743 {
4744 //Call back for vertical kerning spinbutton
4745 }
4747 static void
4748 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4749 {
4750 //Call back for letter rotation spinbutton
4751 }*/
4753 namespace {
4755 bool popdown_visible = false;
4756 bool popdown_hasfocus = false;
4758 void
4759 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4760 {
4761 SPStyle *query =
4762 sp_style_new (SP_ACTIVE_DOCUMENT);
4764 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4766 int result_family =
4767 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4769 int result_style =
4770 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4772 int result_numbers =
4773 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4775 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4777 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4778 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4779 {
4780 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4782 if (repr)
4783 {
4784 sp_style_read_from_repr (query, repr);
4785 }
4786 else
4787 {
4788 return;
4789 }
4790 }
4792 if (query->text)
4793 {
4794 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4795 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4796 gtk_entry_set_text (GTK_ENTRY (entry), "");
4798 } else if (query->text->font_specification.value || query->text->font_family.value) {
4800 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4802 // Get the font that corresponds
4803 Glib::ustring familyName;
4805 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4806 if (font) {
4807 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4808 font->Unref();
4809 font = NULL;
4810 }
4812 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4814 Gtk::TreePath path;
4815 try {
4816 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4817 } catch (...) {
4818 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4819 return;
4820 }
4822 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4823 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4825 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4827 gtk_tree_selection_select_path (tselection, path.gobj());
4828 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4830 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4831 }
4833 //Size
4834 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4835 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4836 g_object_set_data (tbl, "size-block", gpointer(1));
4837 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4838 g_object_set_data (tbl, "size-block", gpointer(0));
4839 free (str);
4841 //Anchor
4842 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4843 {
4844 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4845 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4846 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4847 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4848 }
4849 else
4850 {
4851 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4852 {
4853 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4854 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4855 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4856 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4857 }
4858 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4859 {
4860 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4861 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4862 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4863 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4864 }
4865 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4866 {
4867 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4868 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4869 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4870 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4871 }
4872 }
4874 //Style
4875 {
4876 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4878 gboolean active = gtk_toggle_button_get_active (button);
4879 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4881 if (active != check)
4882 {
4883 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4884 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4885 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4886 }
4887 }
4889 {
4890 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4892 gboolean active = gtk_toggle_button_get_active (button);
4893 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4895 if (active != check)
4896 {
4897 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4898 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4899 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4900 }
4901 }
4903 //Orientation
4904 //locking both buttons, changing one affect all group (both)
4905 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4906 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4908 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4909 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4911 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4912 {
4913 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4914 }
4915 else
4916 {
4917 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4918 }
4919 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4920 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4921 }
4923 sp_style_unref(query);
4924 }
4926 void
4927 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4928 {
4929 sp_text_toolbox_selection_changed (selection, tbl);
4930 }
4932 void
4933 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4934 {
4935 sp_text_toolbox_selection_changed (NULL, tbl);
4936 }
4938 void
4939 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4940 GObject *tbl)
4941 {
4942 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4943 GtkTreeModel *model = 0;
4944 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4945 GtkTreeIter iter;
4946 char *family = 0;
4948 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4949 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4951 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4952 return;
4953 }
4955 gtk_tree_model_get (model, &iter, 0, &family, -1);
4957 if (g_object_get_data (G_OBJECT (selection), "block"))
4958 {
4959 gtk_entry_set_text (GTK_ENTRY (entry), family);
4960 return;
4961 }
4963 gtk_entry_set_text (GTK_ENTRY (entry), family);
4965 SPStyle *query =
4966 sp_style_new (SP_ACTIVE_DOCUMENT);
4968 int result_fontspec =
4969 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4971 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4973 SPCSSAttr *css = sp_repr_css_attr_new ();
4976 // First try to get the font spec from the stored value
4977 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4979 if (fontSpec.empty()) {
4980 // Construct a new font specification if it does not yet exist
4981 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4982 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4983 fontFromStyle->Unref();
4984 }
4986 if (!fontSpec.empty()) {
4987 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4988 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4989 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4990 if (font) {
4991 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4993 // Set all the these just in case they were altered when finding the best
4994 // match for the new family and old style...
4996 gchar c[256];
4998 font->Family(c, 256);
4999 sp_repr_css_set_property (css, "font-family", c);
5001 font->Attribute( "weight", c, 256);
5002 sp_repr_css_set_property (css, "font-weight", c);
5004 font->Attribute("style", c, 256);
5005 sp_repr_css_set_property (css, "font-style", c);
5007 font->Attribute("stretch", c, 256);
5008 sp_repr_css_set_property (css, "font-stretch", c);
5010 font->Attribute("variant", c, 256);
5011 sp_repr_css_set_property (css, "font-variant", c);
5013 font->Unref();
5014 }
5015 }
5016 }
5018 // If querying returned nothing, set the default style of the tool (for new texts)
5019 if (result_fontspec == QUERY_STYLE_NOTHING)
5020 {
5021 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5022 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5023 }
5024 else
5025 {
5026 sp_desktop_set_style (desktop, css, true, true);
5027 }
5029 sp_style_unref(query);
5031 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5032 _("Text: Change font family"));
5033 sp_repr_css_attr_unref (css);
5034 free (family);
5035 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5037 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5038 }
5040 void
5041 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5042 GObject *tbl)
5043 {
5044 const char *family = gtk_entry_get_text (entry);
5046 try {
5047 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5048 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5049 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5050 gtk_tree_selection_select_path (selection, path.gobj());
5051 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5052 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5053 } catch (...) {
5054 if (family && strlen (family))
5055 {
5056 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5057 }
5058 }
5059 }
5061 void
5062 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5063 gpointer data)
5064 {
5065 if (g_object_get_data (G_OBJECT (button), "block")) return;
5066 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5067 int prop = GPOINTER_TO_INT(data);
5069 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5070 SPCSSAttr *css = sp_repr_css_attr_new ();
5072 switch (prop)
5073 {
5074 case 0:
5075 {
5076 sp_repr_css_set_property (css, "text-anchor", "start");
5077 sp_repr_css_set_property (css, "text-align", "start");
5078 break;
5079 }
5080 case 1:
5081 {
5082 sp_repr_css_set_property (css, "text-anchor", "middle");
5083 sp_repr_css_set_property (css, "text-align", "center");
5084 break;
5085 }
5087 case 2:
5088 {
5089 sp_repr_css_set_property (css, "text-anchor", "end");
5090 sp_repr_css_set_property (css, "text-align", "end");
5091 break;
5092 }
5094 case 3:
5095 {
5096 sp_repr_css_set_property (css, "text-anchor", "start");
5097 sp_repr_css_set_property (css, "text-align", "justify");
5098 break;
5099 }
5100 }
5102 SPStyle *query =
5103 sp_style_new (SP_ACTIVE_DOCUMENT);
5104 int result_numbers =
5105 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5107 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5108 if (result_numbers == QUERY_STYLE_NOTHING)
5109 {
5110 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5111 }
5113 sp_style_unref(query);
5115 sp_desktop_set_style (desktop, css, true, true);
5116 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5117 _("Text: Change alignment"));
5118 sp_repr_css_attr_unref (css);
5120 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5121 }
5123 void
5124 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5125 gpointer data)
5126 {
5127 if (g_object_get_data (G_OBJECT (button), "block")) return;
5129 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5130 SPCSSAttr *css = sp_repr_css_attr_new ();
5131 int prop = GPOINTER_TO_INT(data);
5132 bool active = gtk_toggle_button_get_active (button);
5134 SPStyle *query =
5135 sp_style_new (SP_ACTIVE_DOCUMENT);
5137 int result_fontspec =
5138 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5140 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5141 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5142 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5144 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5145 Glib::ustring newFontSpec = "";
5147 if (fontSpec.empty()) {
5148 // Construct a new font specification if it does not yet exist
5149 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5150 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5151 fontFromStyle->Unref();
5152 }
5154 switch (prop)
5155 {
5156 case 0:
5157 {
5158 if (!fontSpec.empty()) {
5159 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5160 }
5161 if (fontSpec != newFontSpec) {
5162 // Don't even set the bold if the font didn't exist on the system
5163 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5164 }
5165 break;
5166 }
5168 case 1:
5169 {
5170 if (!fontSpec.empty()) {
5171 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5172 }
5173 if (fontSpec != newFontSpec) {
5174 // Don't even set the italic if the font didn't exist on the system
5175 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5176 }
5177 break;
5178 }
5179 }
5181 if (!newFontSpec.empty()) {
5182 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5183 }
5185 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5186 if (result_fontspec == QUERY_STYLE_NOTHING)
5187 {
5188 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5189 }
5191 sp_style_unref(query);
5193 sp_desktop_set_style (desktop, css, true, true);
5194 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5195 _("Text: Change font style"));
5196 sp_repr_css_attr_unref (css);
5198 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5199 }
5201 void
5202 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5203 gpointer data)
5204 {
5205 if (g_object_get_data (G_OBJECT (button), "block")) {
5206 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5207 return;
5208 }
5210 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5211 SPCSSAttr *css = sp_repr_css_attr_new ();
5212 int prop = GPOINTER_TO_INT(data);
5214 switch (prop)
5215 {
5216 case 0:
5217 {
5218 sp_repr_css_set_property (css, "writing-mode", "lr");
5219 break;
5220 }
5222 case 1:
5223 {
5224 sp_repr_css_set_property (css, "writing-mode", "tb");
5225 break;
5226 }
5227 }
5229 SPStyle *query =
5230 sp_style_new (SP_ACTIVE_DOCUMENT);
5231 int result_numbers =
5232 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5234 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5235 if (result_numbers == QUERY_STYLE_NOTHING)
5236 {
5237 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5238 }
5240 sp_desktop_set_style (desktop, css, true, true);
5241 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5242 _("Text: Change orientation"));
5243 sp_repr_css_attr_unref (css);
5245 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5246 }
5248 gboolean
5249 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5250 {
5251 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5252 if (!desktop) return FALSE;
5254 switch (get_group0_keyval (event)) {
5255 case GDK_Escape: // defocus
5256 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5257 sp_text_toolbox_selection_changed (NULL, tbl); // update
5258 return TRUE; // I consumed the event
5259 break;
5260 }
5261 return FALSE;
5262 }
5264 gboolean
5265 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5266 {
5267 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5268 if (!desktop) return FALSE;
5270 switch (get_group0_keyval (event)) {
5271 case GDK_KP_Enter:
5272 case GDK_Return:
5273 case GDK_Escape: // defocus
5274 gtk_widget_hide (w);
5275 popdown_visible = false;
5276 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5277 return TRUE; // I consumed the event
5278 break;
5279 case GDK_w:
5280 case GDK_W:
5281 if (event->state & GDK_CONTROL_MASK) {
5282 gtk_widget_hide (w);
5283 popdown_visible = false;
5284 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5285 return TRUE; // I consumed the event
5286 }
5287 break;
5288 }
5289 return FALSE;
5290 }
5293 void
5294 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5295 GObject *tbl)
5296 {
5297 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5299 if (g_object_get_data (tbl, "size-block")) return;
5301 // If this is not from selecting a size in the list (in which case get_active will give the
5302 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5303 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5304 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5305 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5306 return;
5308 gchar *endptr;
5309 gdouble value = -1;
5310 char *text = gtk_combo_box_get_active_text (cbox);
5311 if (text) {
5312 value = g_strtod (text, &endptr);
5313 if (endptr == text) // conversion failed, non-numeric input
5314 value = -1;
5315 free (text);
5316 }
5317 if (value <= 0) {
5318 return; // could not parse value
5319 }
5321 SPCSSAttr *css = sp_repr_css_attr_new ();
5322 Inkscape::CSSOStringStream osfs;
5323 osfs << value;
5324 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5326 SPStyle *query =
5327 sp_style_new (SP_ACTIVE_DOCUMENT);
5328 int result_numbers =
5329 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5331 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5332 if (result_numbers == QUERY_STYLE_NOTHING)
5333 {
5334 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5335 }
5337 sp_style_unref(query);
5339 sp_desktop_set_style (desktop, css, true, true);
5340 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5341 _("Text: Change font size"));
5342 sp_repr_css_attr_unref (css);
5344 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5345 }
5347 gboolean
5348 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5349 {
5350 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5351 if (!desktop) return FALSE;
5353 if (!g_object_get_data (tbl, "esc-pressed")) {
5354 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5355 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5356 sp_text_toolbox_size_changed (cbox, tbl);
5357 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5358 }
5359 return FALSE; // I consumed the event
5360 }
5363 gboolean
5364 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5365 {
5366 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5367 if (!desktop) return FALSE;
5369 switch (get_group0_keyval (event)) {
5370 case GDK_Escape: // defocus
5371 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5372 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5373 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5374 return TRUE; // I consumed the event
5375 break;
5376 case GDK_Return: // defocus
5377 case GDK_KP_Enter:
5378 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5379 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5380 sp_text_toolbox_size_changed (cbox, tbl);
5381 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5382 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5383 return TRUE; // I consumed the event
5384 break;
5385 }
5386 return FALSE;
5387 }
5389 void
5390 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5391 GObject *tbl)
5392 {
5393 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5394 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5395 int x, y;
5397 if (!popdown_visible)
5398 {
5399 gdk_window_get_origin (widget->window, &x, &y);
5400 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5401 gtk_widget_show_all (popdown);
5402 //sp_transientize (popdown);
5404 gdk_pointer_grab (widget->window, TRUE,
5405 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5406 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5407 GDK_POINTER_MOTION_MASK),
5408 NULL, NULL, GDK_CURRENT_TIME);
5410 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5412 popdown_visible = true;
5413 }
5414 else
5415 {
5416 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5417 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5418 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5419 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5420 gtk_widget_hide (popdown);
5421 popdown_visible = false;
5422 }
5423 }
5425 gboolean
5426 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5427 GdkEventFocus */*event*/,
5428 GObject */*tbl*/)
5429 {
5430 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5431 return FALSE;
5432 }
5434 gboolean
5435 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5436 GdkEventFocus */*event*/,
5437 GObject */*tbl*/)
5438 {
5439 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5441 if (popdown_hasfocus) {
5442 gtk_widget_hide (popdown);
5443 popdown_hasfocus = false;
5444 popdown_visible = false;
5445 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5446 return TRUE;
5447 }
5448 return FALSE;
5449 }
5451 gboolean
5452 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5453 GdkEventFocus */*event*/,
5454 GObject */*tbl*/)
5455 {
5456 popdown_hasfocus = true;
5457 return TRUE;
5458 }
5461 void
5462 cell_data_func (GtkTreeViewColumn */*column*/,
5463 GtkCellRenderer *cell,
5464 GtkTreeModel *tree_model,
5465 GtkTreeIter *iter,
5466 gpointer /*data*/)
5467 {
5468 char *family,
5469 *family_escaped,
5470 *sample_escaped;
5472 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5474 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5476 family_escaped = g_markup_escape_text (family, -1);
5477 sample_escaped = g_markup_escape_text (sample, -1);
5479 std::stringstream markup;
5480 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5481 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5483 free (family);
5484 free (family_escaped);
5485 free (sample_escaped);
5486 }
5488 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5489 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5490 if (completion) {
5491 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5492 g_object_unref (completion);
5493 }
5494 }
5496 GtkWidget*
5497 sp_text_toolbox_new (SPDesktop *desktop)
5498 {
5499 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5500 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5502 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5503 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5505 GtkTooltips *tt = gtk_tooltips_new();
5506 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5508 ////////////Family
5509 //Window
5510 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5511 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5513 //Entry
5514 GtkWidget *entry = gtk_entry_new ();
5515 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5516 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5517 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5518 gtk_entry_completion_set_text_column (completion, 0);
5519 gtk_entry_completion_set_minimum_key_length (completion, 1);
5520 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5521 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5522 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5523 gtk_toolbar_append_widget( tbl, entry, "", "" );
5524 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5526 //Button
5527 GtkWidget *button = gtk_button_new ();
5528 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5529 gtk_toolbar_append_widget( tbl, button, "", "");
5531 //Popdown
5532 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5533 GtkWidget *treeview = gtk_tree_view_new ();
5535 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5536 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5537 gtk_tree_view_column_pack_start (column, cell, FALSE);
5538 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5539 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5540 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5542 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5543 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5544 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5546 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5548 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5549 gtk_container_add (GTK_CONTAINER (sw), treeview);
5551 gtk_container_add (GTK_CONTAINER (window), sw);
5552 gtk_widget_set_size_request (window, 300, 450);
5554 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5555 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5556 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5558 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5560 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5561 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5562 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5564 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5565 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5567 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5568 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5569 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5570 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5571 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5573 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5574 GtkWidget *box = gtk_event_box_new ();
5575 gtk_container_add (GTK_CONTAINER (box), image);
5576 gtk_toolbar_append_widget( tbl, box, "", "");
5577 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5578 GtkTooltips *tooltips = gtk_tooltips_new ();
5579 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5580 gtk_widget_hide (GTK_WIDGET (box));
5581 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5583 ////////////Size
5584 const char *sizes[] = {
5585 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5586 "16", "18", "20", "22", "24", "28",
5587 "32", "36", "40", "48", "56", "64", "72", "144"
5588 };
5590 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5591 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5592 gtk_widget_set_size_request (cbox, 80, -1);
5593 gtk_toolbar_append_widget( tbl, cbox, "", "");
5594 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5595 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5596 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5597 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5599 ////////////Text anchor
5600 GtkWidget *group = gtk_radio_button_new (NULL);
5601 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5602 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5604 // left
5605 GtkWidget *rbutton = group;
5606 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5607 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5608 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5610 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5611 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5612 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5613 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5615 // center
5616 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5617 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5618 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5619 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5621 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5622 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5623 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5624 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5626 // right
5627 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5628 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5629 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5630 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5632 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5633 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5634 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5635 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5637 // fill
5638 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5639 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5640 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5641 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5643 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5644 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5645 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5646 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5648 gtk_toolbar_append_widget( tbl, row, "", "");
5650 //spacer
5651 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5653 ////////////Text style
5654 row = gtk_hbox_new (FALSE, 4);
5656 // bold
5657 rbutton = gtk_toggle_button_new ();
5658 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5659 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5660 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5661 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5663 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5664 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5665 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5667 // italic
5668 rbutton = gtk_toggle_button_new ();
5669 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5670 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5671 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5672 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5674 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5675 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5676 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5678 gtk_toolbar_append_widget( tbl, row, "", "");
5680 //spacer
5681 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5683 ////////////Text orientation
5684 group = gtk_radio_button_new (NULL);
5685 row = gtk_hbox_new (FALSE, 4);
5686 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5688 // horizontal
5689 rbutton = group;
5690 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5691 gtk_container_add (GTK_CONTAINER (rbutton),
5692 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5693 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5694 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5696 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5697 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5698 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5700 // vertical
5701 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5702 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5703 gtk_container_add (GTK_CONTAINER (rbutton),
5704 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5705 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5706 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5708 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5709 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5710 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5711 gtk_toolbar_append_widget( tbl, row, "", "" );
5714 //watch selection
5715 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5717 sigc::connection *c_selection_changed =
5718 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5719 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5720 pool->add_connection ("selection-changed", c_selection_changed);
5722 sigc::connection *c_selection_modified =
5723 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5724 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5725 pool->add_connection ("selection-modified", c_selection_modified);
5727 sigc::connection *c_subselection_changed =
5728 new sigc::connection (desktop->connectToolSubselectionChanged
5729 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5730 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5732 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5735 gtk_widget_show_all( GTK_WIDGET(tbl) );
5737 return GTK_WIDGET(tbl);
5738 } // end of sp_text_toolbox_new()
5740 }//<unnamed> namespace
5743 //#########################
5744 //## Connector ##
5745 //#########################
5747 static void sp_connector_path_set_avoid(void)
5748 {
5749 cc_selection_set_avoid(true);
5750 }
5753 static void sp_connector_path_set_ignore(void)
5754 {
5755 cc_selection_set_avoid(false);
5756 }
5760 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5761 {
5762 // quit if run by the _changed callbacks
5763 if (g_object_get_data( tbl, "freeze" )) {
5764 return;
5765 }
5767 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5768 SPDocument *doc = sp_desktop_document(desktop);
5770 if (!sp_document_get_undo_sensitive(doc))
5771 {
5772 return;
5773 }
5775 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5777 if ( repr->attribute("inkscape:connector-spacing") ) {
5778 gdouble priorValue = gtk_adjustment_get_value(adj);
5779 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5780 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5781 return;
5782 }
5783 } else if ( adj->value == defaultConnSpacing ) {
5784 return;
5785 }
5787 // in turn, prevent callbacks from responding
5788 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5790 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5791 SP_OBJECT(desktop->namedview)->updateRepr();
5793 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5794 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5795 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5796 NR::Matrix m = NR::identity();
5797 avoid_item_move(&m, item);
5798 }
5800 if (items) {
5801 g_slist_free(items);
5802 }
5804 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5805 _("Change connector spacing"));
5807 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5809 spinbutton_defocus(GTK_OBJECT(tbl));
5810 }
5812 static void sp_connector_graph_layout(void)
5813 {
5814 if (!SP_ACTIVE_DESKTOP) return;
5816 // hack for clones, see comment in align-and-distribute.cpp
5817 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5818 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5820 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5822 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5824 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5825 }
5827 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5828 {
5829 if ( gtk_toggle_action_get_active( act ) ) {
5830 prefs_set_string_attribute("tools.connector", "directedlayout",
5831 "true");
5832 } else {
5833 prefs_set_string_attribute("tools.connector", "directedlayout",
5834 "false");
5835 }
5836 }
5838 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5839 {
5840 if ( gtk_toggle_action_get_active( act ) ) {
5841 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5842 "true");
5843 } else {
5844 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5845 "false");
5846 }
5847 }
5850 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5851 {
5852 prefs_set_double_attribute("tools.connector", "length", adj->value);
5853 spinbutton_defocus(GTK_OBJECT(tbl));
5854 }
5856 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5857 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5858 bool /*is_interactive*/, gpointer data)
5859 {
5860 GtkWidget *tbl = GTK_WIDGET(data);
5862 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5863 return;
5864 }
5865 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5866 return;
5867 }
5869 GtkAdjustment *adj = (GtkAdjustment*)
5870 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5871 gdouble spacing = defaultConnSpacing;
5872 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5874 gtk_adjustment_set_value(adj, spacing);
5875 }
5878 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5879 NULL, /* child_added */
5880 NULL, /* child_removed */
5881 connector_tb_event_attr_changed,
5882 NULL, /* content_changed */
5883 NULL /* order_changed */
5884 };
5887 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5888 {
5889 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5891 {
5892 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5893 _("Avoid"),
5894 _("Make connectors avoid selected objects"),
5895 "connector_avoid",
5896 secondarySize );
5897 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5898 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5899 }
5901 {
5902 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5903 _("Ignore"),
5904 _("Make connectors ignore selected objects"),
5905 "connector_ignore",
5906 secondarySize );
5907 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5908 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5909 }
5911 EgeAdjustmentAction* eact = 0;
5913 // Spacing spinbox
5914 eact = create_adjustment_action( "ConnectorSpacingAction",
5915 _("Connector Spacing"), _("Spacing:"),
5916 _("The amount of space left around objects by auto-routing connectors"),
5917 "tools.connector", "spacing", defaultConnSpacing,
5918 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5919 0, 100, 1.0, 10.0,
5920 0, 0, 0,
5921 connector_spacing_changed, 1, 0 );
5922 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5924 // Graph (connector network) layout
5925 {
5926 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5927 _("Graph"),
5928 _("Nicely arrange selected connector network"),
5929 "graph_layout",
5930 secondarySize );
5931 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5932 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5933 }
5935 // Default connector length spinbox
5936 eact = create_adjustment_action( "ConnectorLengthAction",
5937 _("Connector Length"), _("Length:"),
5938 _("Ideal length for connectors when layout is applied"),
5939 "tools.connector", "length", 100,
5940 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5941 10, 1000, 10.0, 100.0,
5942 0, 0, 0,
5943 connector_length_changed, 1, 0 );
5944 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5947 // Directed edges toggle button
5948 {
5949 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5950 _("Downwards"),
5951 _("Make connectors with end-markers (arrows) point downwards"),
5952 "directed_graph",
5953 Inkscape::ICON_SIZE_DECORATION );
5954 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5956 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5957 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5958 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5960 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5961 }
5963 // Avoid overlaps toggle button
5964 {
5965 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5966 _("Remove overlaps"),
5967 _("Do not allow overlapping shapes"),
5968 "remove_overlaps",
5969 Inkscape::ICON_SIZE_DECORATION );
5970 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5972 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5973 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5974 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5976 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5977 }
5979 // Code to watch for changes to the connector-spacing attribute in
5980 // the XML.
5981 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5982 g_assert(repr != NULL);
5984 purge_repr_listener( holder, holder );
5986 if (repr) {
5987 g_object_set_data( holder, "repr", repr );
5988 Inkscape::GC::anchor(repr);
5989 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5990 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5991 }
5992 } // end of sp_connector_toolbox_prep()
5995 //#########################
5996 //## Paintbucket ##
5997 //#########################
5999 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6000 {
6001 gint channels = ege_select_one_action_get_active( act );
6002 flood_channels_set_channels( channels );
6003 }
6005 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6006 {
6007 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6008 }
6010 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6011 {
6012 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6013 }
6015 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6016 {
6017 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6018 SPUnit const *unit = tracker->getActiveUnit();
6020 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6022 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6023 }
6025 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
6026 {
6027 // FIXME: make defaults settable via Inkscape Options
6028 struct KeyValue {
6029 char const *key;
6030 double value;
6031 } const key_values[] = {
6032 {"threshold", 15},
6033 {"offset", 0.0}
6034 };
6036 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6037 KeyValue const &kv = key_values[i];
6038 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
6039 if ( adj ) {
6040 gtk_adjustment_set_value(adj, kv.value);
6041 }
6042 }
6044 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
6045 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6046 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
6047 ege_select_one_action_set_active( autogap_action, 0 );
6048 }
6050 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6051 {
6052 EgeAdjustmentAction* eact = 0;
6054 {
6055 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6057 GList* items = 0;
6058 gint count = 0;
6059 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6060 {
6061 GtkTreeIter iter;
6062 gtk_list_store_append( model, &iter );
6063 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6064 count++;
6065 }
6066 g_list_free( items );
6067 items = 0;
6068 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6069 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6070 ege_select_one_action_set_appearance( act1, "compact" );
6071 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6072 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6073 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6074 g_object_set_data( holder, "channels_action", act1 );
6075 }
6077 // Spacing spinbox
6078 {
6079 eact = create_adjustment_action(
6080 "ThresholdAction",
6081 _("Fill Threshold"), _("Threshold:"),
6082 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6083 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6084 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6085 0, 0, 0,
6086 paintbucket_threshold_changed, 1, 0 );
6088 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6089 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6090 }
6092 // Create the units menu.
6093 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6094 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6095 if (stored_unit)
6096 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6097 g_object_set_data( holder, "tracker", tracker );
6098 {
6099 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6100 gtk_action_group_add_action( mainActions, act );
6101 }
6103 // Offset spinbox
6104 {
6105 eact = create_adjustment_action(
6106 "OffsetAction",
6107 _("Grow/shrink by"), _("Grow/shrink by:"),
6108 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6109 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6110 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6111 0, 0, 0,
6112 paintbucket_offset_changed, 1, 2);
6113 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6115 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6116 }
6118 /* Auto Gap */
6119 {
6120 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6122 GList* items = 0;
6123 gint count = 0;
6124 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6125 {
6126 GtkTreeIter iter;
6127 gtk_list_store_append( model, &iter );
6128 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6129 count++;
6130 }
6131 g_list_free( items );
6132 items = 0;
6133 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6134 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6135 ege_select_one_action_set_appearance( act2, "compact" );
6136 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6137 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6138 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6139 g_object_set_data( holder, "autogap_action", act2 );
6140 }
6142 /* Reset */
6143 {
6144 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6145 _("Defaults"),
6146 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6147 GTK_STOCK_CLEAR );
6148 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6149 gtk_action_group_add_action( mainActions, act );
6150 gtk_action_set_sensitive( act, TRUE );
6151 }
6153 }
6155 /*
6156 Local Variables:
6157 mode:c++
6158 c-file-style:"stroustrup"
6159 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6160 indent-tabs-mode:nil
6161 fill-column:99
6162 End:
6163 */
6164 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :