8fa52c2815ab29a01a8f8e8db5101c99027a09a7
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(GTK_STOCK_ABOUT), 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 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
531 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
532 return widg;
533 }
535 Gtk::Widget* VerbAction::create_tool_item_vfunc()
536 {
537 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
538 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
539 GtkWidget* toolbox = 0;
540 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
541 SP_BUTTON_TYPE_TOGGLE,
542 verb,
543 verb2,
544 view,
545 tooltips );
546 if ( active ) {
547 sp_button_toggle_set_down( SP_BUTTON(button), active);
548 }
549 gtk_widget_show_all( button );
550 Gtk::Widget* wrapped = Glib::wrap(button);
551 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
552 holder->add(*wrapped);
554 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
555 return holder;
556 }
558 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
559 {
560 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
561 Gtk::Action::connect_proxy_vfunc(proxy);
562 }
564 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
565 {
566 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
567 Gtk::Action::disconnect_proxy_vfunc(proxy);
568 }
570 void VerbAction::set_active(bool active)
571 {
572 this->active = active;
573 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
574 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
575 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
576 if (ti) {
577 // *should* have one child that is the SPButton
578 Gtk::Widget* child = ti->get_child();
579 if ( child && SP_IS_BUTTON(child->gobj()) ) {
580 SPButton* button = SP_BUTTON(child->gobj());
581 sp_button_toggle_set_down( button, active );
582 }
583 }
584 }
585 }
587 void VerbAction::on_activate()
588 {
589 if ( verb ) {
590 SPAction *action = verb->get_action(view);
591 if ( action ) {
592 sp_action_perform(action, 0);
593 }
594 }
595 }
597 /* Global text entry widgets necessary for update */
598 /* GtkWidget *dropper_rgb_entry,
599 *dropper_opacity_entry ; */
600 // should be made a private member once this is converted to class
602 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
603 connection->disconnect();
604 delete connection;
605 }
607 static void purge_repr_listener( GObject* obj, GObject* tbl )
608 {
609 (void)obj;
610 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
611 if (oldrepr) { // remove old listener
612 sp_repr_remove_listener_by_data(oldrepr, tbl);
613 Inkscape::GC::release(oldrepr);
614 oldrepr = 0;
615 g_object_set_data( tbl, "repr", NULL );
616 }
617 }
619 GtkWidget *
620 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
621 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
622 Inkscape::UI::View::View *view, GtkTooltips *tt)
623 {
624 SPAction *action = verb->get_action(view);
625 if (!action) return NULL;
627 SPAction *doubleclick_action;
628 if (doubleclick_verb)
629 doubleclick_action = doubleclick_verb->get_action(view);
630 else
631 doubleclick_action = NULL;
633 /* fixme: Handle sensitive/unsensitive */
634 /* fixme: Implement sp_button_new_from_action */
635 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
636 gtk_widget_show(b);
639 unsigned int shortcut = sp_shortcut_get_primary(verb);
640 if (shortcut) {
641 gchar key[256];
642 sp_ui_shortcut_string(shortcut, key);
643 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
644 if ( t ) {
645 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
646 }
647 g_free(tip);
648 } else {
649 if ( t ) {
650 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
651 }
652 }
654 return b;
655 }
658 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
659 {
660 SPAction* targetAction = SP_ACTION(user_data);
661 if ( targetAction ) {
662 sp_action_perform( targetAction, NULL );
663 }
664 }
666 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
667 {
668 if ( data ) {
669 GtkAction* act = GTK_ACTION(data);
670 gtk_action_set_sensitive( act, sensitive );
671 }
672 }
674 static SPActionEventVector action_event_vector = {
675 {NULL},
676 NULL,
677 NULL,
678 sp_action_action_set_sensitive,
679 NULL,
680 NULL
681 };
683 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
684 {
685 GtkAction* act = 0;
687 SPAction* targetAction = verb->get_action(view);
688 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
689 act = GTK_ACTION(inky);
690 gtk_action_set_sensitive( act, targetAction->sensitive );
692 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
694 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
695 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
697 return act;
698 }
700 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
701 {
702 Inkscape::UI::View::View *view = desktop;
703 gint verbsToUse[] = {
704 // disabled until we have icons for them:
705 //find
706 //SP_VERB_EDIT_TILE,
707 //SP_VERB_EDIT_UNTILE,
708 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
709 SP_VERB_DIALOG_DISPLAY,
710 SP_VERB_DIALOG_FILL_STROKE,
711 SP_VERB_DIALOG_NAMEDVIEW,
712 SP_VERB_DIALOG_TEXT,
713 SP_VERB_DIALOG_XML_EDITOR,
714 SP_VERB_EDIT_CLONE,
715 SP_VERB_EDIT_COPY,
716 SP_VERB_EDIT_CUT,
717 SP_VERB_EDIT_DUPLICATE,
718 SP_VERB_EDIT_PASTE,
719 SP_VERB_EDIT_REDO,
720 SP_VERB_EDIT_UNDO,
721 SP_VERB_EDIT_UNLINK_CLONE,
722 SP_VERB_FILE_EXPORT,
723 SP_VERB_FILE_IMPORT,
724 SP_VERB_FILE_NEW,
725 SP_VERB_FILE_OPEN,
726 SP_VERB_FILE_PRINT,
727 SP_VERB_FILE_SAVE,
728 SP_VERB_OBJECT_TO_CURVE,
729 SP_VERB_SELECTION_GROUP,
730 SP_VERB_SELECTION_OUTLINE,
731 SP_VERB_SELECTION_UNGROUP,
732 SP_VERB_ZOOM_1_1,
733 SP_VERB_ZOOM_1_2,
734 SP_VERB_ZOOM_2_1,
735 SP_VERB_ZOOM_DRAWING,
736 SP_VERB_ZOOM_IN,
737 SP_VERB_ZOOM_NEXT,
738 SP_VERB_ZOOM_OUT,
739 SP_VERB_ZOOM_PAGE,
740 SP_VERB_ZOOM_PAGE_WIDTH,
741 SP_VERB_ZOOM_PREV,
742 SP_VERB_ZOOM_SELECTION,
743 };
745 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
747 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
748 Glib::RefPtr<Gtk::ActionGroup> mainActions;
749 if ( groups.find(desktop) != groups.end() ) {
750 mainActions = groups[desktop];
751 }
753 if ( !mainActions ) {
754 mainActions = Gtk::ActionGroup::create("main");
755 groups[desktop] = mainActions;
756 }
758 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
759 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
760 if ( verb ) {
761 if (!mainActions->get_action(verb->get_id())) {
762 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
763 mainActions->add(Glib::wrap(act));
764 }
765 }
766 }
768 if ( !mainActions->get_action("ToolZoom") ) {
769 GtkTooltips *tt = gtk_tooltips_new();
770 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
771 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
772 if ( va ) {
773 mainActions->add(va);
774 if ( i == 0 ) {
775 va->set_active(true);
776 }
777 }
778 }
779 }
782 return mainActions;
783 }
786 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
787 {
788 gtk_widget_set_size_request( widget,
789 widget->allocation.width,
790 widget->allocation.height );
791 }
793 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
794 {
795 gtk_widget_set_size_request( widget, -1, -1 );
796 }
800 GtkWidget *
801 sp_tool_toolbox_new()
802 {
803 GtkTooltips *tt = gtk_tooltips_new();
804 GtkWidget* tb = gtk_toolbar_new();
805 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
806 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
808 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
809 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
811 gtk_widget_set_sensitive(tb, FALSE);
813 GtkWidget *hb = gtk_handle_box_new();
814 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
815 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
816 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
818 gtk_container_add(GTK_CONTAINER(hb), tb);
819 gtk_widget_show(GTK_WIDGET(tb));
821 sigc::connection* conn = new sigc::connection;
822 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
824 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
825 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
827 return hb;
828 }
830 GtkWidget *
831 sp_aux_toolbox_new()
832 {
833 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
835 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
837 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
839 gtk_widget_set_sensitive(tb, FALSE);
841 GtkWidget *hb = gtk_handle_box_new();
842 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
843 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
844 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
846 gtk_container_add(GTK_CONTAINER(hb), tb);
847 gtk_widget_show(GTK_WIDGET(tb));
849 sigc::connection* conn = new sigc::connection;
850 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
852 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
853 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
855 return hb;
856 }
858 //####################################
859 //# Commands Bar
860 //####################################
862 GtkWidget *
863 sp_commands_toolbox_new()
864 {
865 GtkWidget *tb = gtk_toolbar_new();
867 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
868 gtk_widget_set_sensitive(tb, FALSE);
870 GtkWidget *hb = gtk_handle_box_new();
871 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
872 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
873 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
875 gtk_container_add(GTK_CONTAINER(hb), tb);
876 gtk_widget_show(GTK_WIDGET(tb));
878 sigc::connection* conn = new sigc::connection;
879 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
881 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
882 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
884 return hb;
885 }
888 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
889 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
890 gchar const *path, gchar const *data, gdouble def,
891 GtkWidget *focusTarget,
892 GtkWidget *us,
893 GObject *dataKludge,
894 gboolean altx, gchar const *altx_mark,
895 gdouble lower, gdouble upper, gdouble step, gdouble page,
896 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
897 void (*callback)(GtkAdjustment *, GObject *),
898 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
899 {
900 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
901 lower, upper, step, page, page ) );
902 if (us) {
903 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
904 }
906 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
908 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
909 if ( shortLabel ) {
910 g_object_set( act, "short_label", shortLabel, NULL );
911 }
913 if ( (descrCount > 0) && descrLabels && descrValues ) {
914 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
915 }
917 if ( focusTarget ) {
918 ege_adjustment_action_set_focuswidget( act, focusTarget );
919 }
921 if ( altx && altx_mark ) {
922 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
923 }
925 if ( dataKludge ) {
926 g_object_set_data( dataKludge, data, adj );
927 }
929 // Using a cast just to make sure we pass in the right kind of function pointer
930 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
932 return act;
933 }
936 //####################################
937 //# node editing callbacks
938 //####################################
940 /**
941 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
942 */
943 static ShapeEditor *get_current_shape_editor()
944 {
945 if (!SP_ACTIVE_DESKTOP) {
946 return NULL;
947 }
949 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
951 if (!SP_IS_NODE_CONTEXT(event_context)) {
952 return NULL;
953 }
955 return SP_NODE_CONTEXT(event_context)->shape_editor;
956 }
959 void
960 sp_node_path_edit_add(void)
961 {
962 ShapeEditor *shape_editor = get_current_shape_editor();
963 if (shape_editor) shape_editor->add_node();
964 }
966 void
967 sp_node_path_edit_delete(void)
968 {
969 ShapeEditor *shape_editor = get_current_shape_editor();
970 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
971 }
973 void
974 sp_node_path_edit_delete_segment(void)
975 {
976 ShapeEditor *shape_editor = get_current_shape_editor();
977 if (shape_editor) shape_editor->delete_segment();
978 }
980 void
981 sp_node_path_edit_break(void)
982 {
983 ShapeEditor *shape_editor = get_current_shape_editor();
984 if (shape_editor) shape_editor->break_at_nodes();
985 }
987 void
988 sp_node_path_edit_join(void)
989 {
990 ShapeEditor *shape_editor = get_current_shape_editor();
991 if (shape_editor) shape_editor->join_nodes();
992 }
994 void
995 sp_node_path_edit_join_segment(void)
996 {
997 ShapeEditor *shape_editor = get_current_shape_editor();
998 if (shape_editor) shape_editor->join_segments();
999 }
1001 void
1002 sp_node_path_edit_toline(void)
1003 {
1004 ShapeEditor *shape_editor = get_current_shape_editor();
1005 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1006 }
1008 void
1009 sp_node_path_edit_tocurve(void)
1010 {
1011 ShapeEditor *shape_editor = get_current_shape_editor();
1012 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1013 }
1015 void
1016 sp_node_path_edit_cusp(void)
1017 {
1018 ShapeEditor *shape_editor = get_current_shape_editor();
1019 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1020 }
1022 void
1023 sp_node_path_edit_smooth(void)
1024 {
1025 ShapeEditor *shape_editor = get_current_shape_editor();
1026 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1027 }
1029 void
1030 sp_node_path_edit_symmetrical(void)
1031 {
1032 ShapeEditor *shape_editor = get_current_shape_editor();
1033 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1034 }
1036 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1037 bool show = gtk_toggle_action_get_active( act );
1038 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1039 ShapeEditor *shape_editor = get_current_shape_editor();
1040 if (shape_editor) shape_editor->show_handles(show);
1041 }
1043 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1044 bool show = gtk_toggle_action_get_active( act );
1045 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1046 ShapeEditor *shape_editor = get_current_shape_editor();
1047 if (shape_editor) shape_editor->show_helperpath(show);
1048 }
1050 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1051 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1052 }
1054 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1055 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1056 }
1058 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1059 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1060 }
1062 /* is called when the node selection is modified */
1063 static void
1064 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1065 {
1066 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1067 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1068 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1069 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1071 // quit if run by the attr_changed listener
1072 if (g_object_get_data( tbl, "freeze" )) {
1073 return;
1074 }
1076 // in turn, prevent listener from responding
1077 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1079 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1080 SPUnit const *unit = tracker->getActiveUnit();
1082 ShapeEditor *shape_editor = get_current_shape_editor();
1083 if (shape_editor && shape_editor->has_nodepath()) {
1084 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1085 int n_selected = 0;
1086 if (nodepath) {
1087 n_selected = nodepath->numSelected();
1088 }
1090 if (n_selected == 0) {
1091 gtk_action_set_sensitive(xact, FALSE);
1092 gtk_action_set_sensitive(yact, FALSE);
1093 } else {
1094 gtk_action_set_sensitive(xact, TRUE);
1095 gtk_action_set_sensitive(yact, TRUE);
1096 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1097 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1099 if (n_selected == 1) {
1100 NR::Point sel_node = nodepath->singleSelectedCoords();
1101 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1102 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1103 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1104 }
1105 } else {
1106 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1107 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1108 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1109 /* Note: Currently x and y will always have a value, even if the coordinates of the
1110 selected nodes don't coincide (in this case we use the coordinates of the center
1111 of the bounding box). So the entries are never set to zero. */
1112 // FIXME: Maybe we should clear the entry if several nodes are selected
1113 // instead of providing a kind of average value
1114 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1115 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1116 }
1117 }
1118 }
1119 } else {
1120 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1121 gtk_action_set_sensitive(xact, FALSE);
1122 gtk_action_set_sensitive(yact, FALSE);
1123 }
1125 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1126 }
1128 static void
1129 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1130 {
1131 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1133 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1134 SPUnit const *unit = tracker->getActiveUnit();
1136 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1137 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1138 }
1140 // quit if run by the attr_changed listener
1141 if (g_object_get_data( tbl, "freeze" )) {
1142 return;
1143 }
1145 // in turn, prevent listener from responding
1146 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1148 ShapeEditor *shape_editor = get_current_shape_editor();
1149 if (shape_editor && shape_editor->has_nodepath()) {
1150 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1151 if (!strcmp(value_name, "x")) {
1152 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1153 }
1154 if (!strcmp(value_name, "y")) {
1155 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1156 }
1157 }
1159 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1160 }
1162 static void
1163 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1164 {
1165 sp_node_path_value_changed(adj, tbl, "x");
1166 }
1168 static void
1169 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1170 {
1171 sp_node_path_value_changed(adj, tbl, "y");
1172 }
1174 void
1175 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1176 {
1177 {
1178 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1179 SPItem *item = selection->singleItem();
1180 if (item && SP_IS_LPE_ITEM(item)) {
1181 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1182 gtk_action_set_sensitive(w, TRUE);
1183 } else {
1184 gtk_action_set_sensitive(w, FALSE);
1185 }
1186 } else {
1187 gtk_action_set_sensitive(w, FALSE);
1188 }
1189 }
1191 {
1192 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1193 SPItem *item = selection->singleItem();
1194 if (item && item->clip_ref && item->clip_ref->getObject()) {
1195 gtk_action_set_sensitive(w, TRUE);
1196 } else {
1197 gtk_action_set_sensitive(w, FALSE);
1198 }
1199 }
1201 {
1202 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1203 SPItem *item = selection->singleItem();
1204 if (item && item->mask_ref && item->mask_ref->getObject()) {
1205 gtk_action_set_sensitive(w, TRUE);
1206 } else {
1207 gtk_action_set_sensitive(w, FALSE);
1208 }
1209 }
1210 }
1212 void
1213 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1214 {
1215 sp_node_toolbox_sel_changed (selection, tbl);
1216 }
1220 //################################
1221 //## Node Editing Toolbox ##
1222 //################################
1224 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1225 {
1226 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1227 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1228 g_object_set_data( holder, "tracker", tracker );
1230 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1232 {
1233 InkAction* inky = ink_action_new( "NodeInsertAction",
1234 _("Insert node"),
1235 _("Insert new nodes into selected segments"),
1236 "node_insert",
1237 secondarySize );
1238 g_object_set( inky, "short_label", _("Insert"), NULL );
1239 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1240 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1241 }
1243 {
1244 InkAction* inky = ink_action_new( "NodeDeleteAction",
1245 _("Delete node"),
1246 _("Delete selected nodes"),
1247 "node_delete",
1248 secondarySize );
1249 g_object_set( inky, "short_label", _("Delete"), NULL );
1250 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1251 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1252 }
1254 {
1255 InkAction* inky = ink_action_new( "NodeJoinAction",
1256 _("Join endnodes"),
1257 _("Join selected endnodes"),
1258 "node_join",
1259 secondarySize );
1260 g_object_set( inky, "short_label", _("Join"), NULL );
1261 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1262 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1263 }
1265 {
1266 InkAction* inky = ink_action_new( "NodeBreakAction",
1267 _("Break nodes"),
1268 _("Break path at selected nodes"),
1269 "node_break",
1270 secondarySize );
1271 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1272 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1273 }
1276 {
1277 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1278 _("Join with segment"),
1279 _("Join selected endnodes with a new segment"),
1280 "node_join_segment",
1281 secondarySize );
1282 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1283 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1284 }
1286 {
1287 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1288 _("Delete segment"),
1289 _("Delete segment between two non-endpoint nodes"),
1290 "node_delete_segment",
1291 secondarySize );
1292 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1293 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1294 }
1296 {
1297 InkAction* inky = ink_action_new( "NodeCuspAction",
1298 _("Node Cusp"),
1299 _("Make selected nodes corner"),
1300 "node_cusp",
1301 secondarySize );
1302 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1303 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1304 }
1306 {
1307 InkAction* inky = ink_action_new( "NodeSmoothAction",
1308 _("Node Smooth"),
1309 _("Make selected nodes smooth"),
1310 "node_smooth",
1311 secondarySize );
1312 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1313 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1314 }
1316 {
1317 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1318 _("Node Symmetric"),
1319 _("Make selected nodes symmetric"),
1320 "node_symmetric",
1321 secondarySize );
1322 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1323 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1324 }
1326 {
1327 InkAction* inky = ink_action_new( "NodeLineAction",
1328 _("Node Line"),
1329 _("Make selected segments lines"),
1330 "node_line",
1331 secondarySize );
1332 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1333 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1334 }
1336 {
1337 InkAction* inky = ink_action_new( "NodeCurveAction",
1338 _("Node Curve"),
1339 _("Make selected segments curves"),
1340 "node_curve",
1341 secondarySize );
1342 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1343 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1344 }
1346 {
1347 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1348 _("Show Handles"),
1349 _("Show the Bezier handles of selected nodes"),
1350 "nodes_show_handles",
1351 Inkscape::ICON_SIZE_DECORATION );
1352 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1353 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1354 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1355 }
1357 {
1358 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1359 _("Show Outline"),
1360 _("Show the outline of the path"),
1361 "nodes_show_helperpath",
1362 Inkscape::ICON_SIZE_DECORATION );
1363 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1364 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1365 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1366 }
1368 {
1369 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1370 _("Next path effect parameter"),
1371 _("Show next path effect parameter for editing"),
1372 "edit_next_parameter",
1373 Inkscape::ICON_SIZE_DECORATION );
1374 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1375 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1376 g_object_set_data( holder, "nodes_lpeedit", inky);
1377 }
1379 {
1380 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1381 _("Edit clipping path"),
1382 _("Edit the clipping path of the object"),
1383 "nodeedit-clippath",
1384 Inkscape::ICON_SIZE_DECORATION );
1385 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1386 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1387 g_object_set_data( holder, "nodes_clippathedit", inky);
1388 }
1390 {
1391 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1392 _("Edit mask path"),
1393 _("Edit the mask of the object"),
1394 "nodeedit-mask",
1395 Inkscape::ICON_SIZE_DECORATION );
1396 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1397 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398 g_object_set_data( holder, "nodes_maskedit", inky);
1399 }
1401 /* X coord of selected node(s) */
1402 {
1403 EgeAdjustmentAction* eact = 0;
1404 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1405 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1406 eact = create_adjustment_action( "NodeXAction",
1407 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1408 "tools.nodes", "Xcoord", 0,
1409 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1410 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1411 labels, values, G_N_ELEMENTS(labels),
1412 sp_node_path_x_value_changed );
1413 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1414 g_object_set_data( holder, "nodes_x_action", eact );
1415 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1416 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1417 }
1419 /* Y coord of selected node(s) */
1420 {
1421 EgeAdjustmentAction* eact = 0;
1422 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1423 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1424 eact = create_adjustment_action( "NodeYAction",
1425 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1426 "tools.nodes", "Ycoord", 0,
1427 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1428 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1429 labels, values, G_N_ELEMENTS(labels),
1430 sp_node_path_y_value_changed );
1431 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1432 g_object_set_data( holder, "nodes_y_action", eact );
1433 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1434 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1435 }
1437 // add the units menu
1438 {
1439 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1440 gtk_action_group_add_action( mainActions, act );
1441 }
1444 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1446 //watch selection
1447 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1449 sigc::connection *c_selection_changed =
1450 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1451 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1452 pool->add_connection ("selection-changed", c_selection_changed);
1454 sigc::connection *c_selection_modified =
1455 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1456 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1457 pool->add_connection ("selection-modified", c_selection_modified);
1459 sigc::connection *c_subselection_changed =
1460 new sigc::connection (desktop->connectToolSubselectionChanged
1461 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1462 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1464 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1466 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1467 } // end of sp_node_toolbox_prep()
1470 //########################
1471 //## Zoom Toolbox ##
1472 //########################
1474 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1475 {
1476 // no custom GtkAction setup needed
1477 } // end of sp_zoom_toolbox_prep()
1479 void
1480 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1481 {
1482 toolbox_set_desktop(toolbox,
1483 desktop,
1484 setup_tool_toolbox,
1485 update_tool_toolbox,
1486 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1487 "event_context_connection")));
1488 }
1491 void
1492 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1493 {
1494 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1495 desktop,
1496 setup_aux_toolbox,
1497 update_aux_toolbox,
1498 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1499 "event_context_connection")));
1500 }
1502 void
1503 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1504 {
1505 toolbox_set_desktop(toolbox,
1506 desktop,
1507 setup_commands_toolbox,
1508 update_commands_toolbox,
1509 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1510 "event_context_connection")));
1511 }
1513 static void
1514 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1515 {
1516 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1517 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1519 if (old_desktop) {
1520 GList *children, *iter;
1522 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1523 for ( iter = children ; iter ; iter = iter->next ) {
1524 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1525 }
1526 g_list_free(children);
1527 }
1529 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1531 if (desktop) {
1532 gtk_widget_set_sensitive(toolbox, TRUE);
1533 setup_func(toolbox, desktop);
1534 update_func(desktop, desktop->event_context, toolbox);
1535 *conn = desktop->connectEventContextChanged
1536 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1537 } else {
1538 gtk_widget_set_sensitive(toolbox, FALSE);
1539 }
1541 } // end of toolbox_set_desktop()
1544 static void
1545 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1546 {
1547 gchar const * descr =
1548 "<ui>"
1549 " <toolbar name='ToolToolbar'>"
1550 " <toolitem action='ToolSelector' />"
1551 " <toolitem action='ToolNode' />"
1552 " <toolitem action='ToolTweak' />"
1553 " <toolitem action='ToolZoom' />"
1554 " <toolitem action='ToolRect' />"
1555 " <toolitem action='Tool3DBox' />"
1556 " <toolitem action='ToolArc' />"
1557 " <toolitem action='ToolStar' />"
1558 " <toolitem action='ToolSpiral' />"
1559 " <toolitem action='ToolPencil' />"
1560 " <toolitem action='ToolPen' />"
1561 " <toolitem action='ToolCalligraphic' />"
1562 " <toolitem action='ToolEraser' />"
1563 " <toolitem action='ToolPaintBucket' />"
1564 " <toolitem action='ToolText' />"
1565 " <toolitem action='ToolConnector' />"
1566 " <toolitem action='ToolGradient' />"
1567 " <toolitem action='ToolDropper' />"
1568 " </toolbar>"
1569 "</ui>";
1570 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1571 GtkUIManager* mgr = gtk_ui_manager_new();
1572 GError* errVal = 0;
1574 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1575 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1577 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1578 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1579 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1580 }
1581 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1582 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1584 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1585 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1587 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1589 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1590 if ( child ) {
1591 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1592 }
1594 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1595 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1596 }
1599 static void
1600 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
1601 {
1602 gchar const *const tname = ( eventcontext
1603 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1604 : NULL );
1605 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1607 for (int i = 0 ; tools[i].type_name ; i++ ) {
1608 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1609 if ( act ) {
1610 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1611 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1612 if ( verbAct ) {
1613 verbAct->set_active(setActive);
1614 }
1615 }
1616 }
1617 }
1619 static void
1620 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1621 {
1622 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1623 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1624 GtkUIManager* mgr = gtk_ui_manager_new();
1625 GError* errVal = 0;
1626 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1627 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1629 std::map<std::string, GtkWidget*> dataHolders;
1631 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1632 if ( aux_toolboxes[i].prep_func ) {
1633 // converted to GtkActions and UIManager
1635 GtkWidget* kludge = gtk_toolbar_new();
1636 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1637 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1638 dataHolders[aux_toolboxes[i].type_name] = kludge;
1639 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1640 } else {
1642 GtkWidget *sub_toolbox = 0;
1643 if (aux_toolboxes[i].create_func == NULL)
1644 sub_toolbox = sp_empty_toolbox_new(desktop);
1645 else {
1646 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1647 }
1649 gtk_size_group_add_widget( grouper, sub_toolbox );
1651 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1652 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1654 }
1655 }
1657 // Second pass to create toolbars *after* all GtkActions are created
1658 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1659 if ( aux_toolboxes[i].prep_func ) {
1660 // converted to GtkActions and UIManager
1662 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1664 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1665 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1667 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1668 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1669 g_free( tmp );
1670 tmp = 0;
1672 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1673 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1674 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1675 }
1676 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1679 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1681 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1682 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1683 swatch->setDesktop( desktop );
1684 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1685 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1686 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1687 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 );
1688 }
1690 gtk_widget_show_all( holder );
1691 sp_set_font_size_smaller( holder );
1693 gtk_size_group_add_widget( grouper, holder );
1695 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1696 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1697 }
1698 }
1700 g_object_unref( G_OBJECT(grouper) );
1701 }
1703 static void
1704 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1705 {
1706 gchar const *tname = ( eventcontext
1707 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1708 : NULL );
1709 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1710 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1711 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1712 gtk_widget_show_all(sub_toolbox);
1713 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1714 } else {
1715 gtk_widget_hide(sub_toolbox);
1716 }
1717 }
1718 }
1720 static void
1721 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1722 {
1723 gchar const * descr =
1724 "<ui>"
1725 " <toolbar name='CommandsToolbar'>"
1726 " <toolitem action='FileNew' />"
1727 " <toolitem action='FileOpen' />"
1728 " <toolitem action='FileSave' />"
1729 " <toolitem action='FilePrint' />"
1730 " <separator />"
1731 " <toolitem action='FileImport' />"
1732 " <toolitem action='FileExport' />"
1733 " <separator />"
1734 " <toolitem action='EditUndo' />"
1735 " <toolitem action='EditRedo' />"
1736 " <separator />"
1737 " <toolitem action='EditCopy' />"
1738 " <toolitem action='EditCut' />"
1739 " <toolitem action='EditPaste' />"
1740 " <separator />"
1741 " <toolitem action='ZoomSelection' />"
1742 " <toolitem action='ZoomDrawing' />"
1743 " <toolitem action='ZoomPage' />"
1744 " <separator />"
1745 " <toolitem action='EditDuplicate' />"
1746 " <toolitem action='EditClone' />"
1747 " <toolitem action='EditUnlinkClone' />"
1748 " <separator />"
1749 " <toolitem action='SelectionGroup' />"
1750 " <toolitem action='SelectionUnGroup' />"
1751 " <separator />"
1752 " <toolitem action='DialogFillStroke' />"
1753 " <toolitem action='DialogText' />"
1754 " <toolitem action='DialogXMLEditor' />"
1755 " <toolitem action='DialogAlignDistribute' />"
1756 " <separator />"
1757 " <toolitem action='DialogPreferences' />"
1758 " <toolitem action='DialogDocumentProperties' />"
1759 " </toolbar>"
1760 "</ui>";
1761 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1764 GtkUIManager* mgr = gtk_ui_manager_new();
1765 GError* errVal = 0;
1767 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1768 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1770 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1771 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1772 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1773 }
1775 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1776 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1778 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1779 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1782 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1784 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1785 if ( child ) {
1786 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1787 }
1789 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1790 }
1792 static void
1793 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1794 {
1795 }
1797 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1798 {
1799 gtk_widget_show(toolbox_toplevel);
1800 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1802 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1803 if (!shown_toolbox) {
1804 return;
1805 }
1806 gtk_widget_show(toolbox);
1808 gtk_widget_show_all(shown_toolbox);
1809 }
1811 static GtkWidget *
1812 sp_empty_toolbox_new(SPDesktop *desktop)
1813 {
1814 GtkWidget *tbl = gtk_toolbar_new();
1815 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1816 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1818 gtk_widget_show_all(tbl);
1819 sp_set_font_size_smaller (tbl);
1821 return tbl;
1822 }
1824 #define MODE_LABEL_WIDTH 70
1826 //########################
1827 //## Star ##
1828 //########################
1830 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1831 {
1832 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1834 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1835 // do not remember prefs if this call is initiated by an undo change, because undoing object
1836 // creation sets bogus values to its attributes before it is deleted
1837 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1838 }
1840 // quit if run by the attr_changed listener
1841 if (g_object_get_data( dataKludge, "freeze" )) {
1842 return;
1843 }
1845 // in turn, prevent listener from responding
1846 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1848 bool modmade = false;
1850 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1851 GSList const *items = selection->itemList();
1852 for (; items != NULL; items = items->next) {
1853 if (SP_IS_STAR((SPItem *) items->data)) {
1854 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1855 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1856 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1857 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1858 + M_PI / (gint)adj->value));
1859 SP_OBJECT((SPItem *) items->data)->updateRepr();
1860 modmade = true;
1861 }
1862 }
1863 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1864 _("Star: Change number of corners"));
1866 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1867 }
1869 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1870 {
1871 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1873 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1874 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1875 }
1877 // quit if run by the attr_changed listener
1878 if (g_object_get_data( dataKludge, "freeze" )) {
1879 return;
1880 }
1882 // in turn, prevent listener from responding
1883 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1885 bool modmade = false;
1886 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1887 GSList const *items = selection->itemList();
1888 for (; items != NULL; items = items->next) {
1889 if (SP_IS_STAR((SPItem *) items->data)) {
1890 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1892 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1893 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1894 if (r2 < r1) {
1895 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1896 } else {
1897 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1898 }
1900 SP_OBJECT((SPItem *) items->data)->updateRepr();
1901 modmade = true;
1902 }
1903 }
1905 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1906 _("Star: Change spoke ratio"));
1908 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1909 }
1911 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1912 {
1913 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1914 bool flat = ege_select_one_action_get_active( act ) == 0;
1916 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1917 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1918 flat ? "true" : "false" );
1919 }
1921 // quit if run by the attr_changed listener
1922 if (g_object_get_data( dataKludge, "freeze" )) {
1923 return;
1924 }
1926 // in turn, prevent listener from responding
1927 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1929 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1930 GSList const *items = selection->itemList();
1931 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1932 bool modmade = false;
1934 if ( prop_action ) {
1935 gtk_action_set_sensitive( prop_action, !flat );
1936 }
1938 for (; items != NULL; items = items->next) {
1939 if (SP_IS_STAR((SPItem *) items->data)) {
1940 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1941 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1942 SP_OBJECT((SPItem *) items->data)->updateRepr();
1943 modmade = true;
1944 }
1945 }
1947 if (modmade) {
1948 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1949 flat ? _("Make polygon") : _("Make star"));
1950 }
1952 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1953 }
1955 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1956 {
1957 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1959 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1960 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1961 }
1963 // quit if run by the attr_changed listener
1964 if (g_object_get_data( dataKludge, "freeze" )) {
1965 return;
1966 }
1968 // in turn, prevent listener from responding
1969 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1971 bool modmade = false;
1973 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1974 GSList const *items = selection->itemList();
1975 for (; items != NULL; items = items->next) {
1976 if (SP_IS_STAR((SPItem *) items->data)) {
1977 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1978 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1979 SP_OBJECT(items->data)->updateRepr();
1980 modmade = true;
1981 }
1982 }
1983 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1984 _("Star: Change rounding"));
1986 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1987 }
1989 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1990 {
1991 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1993 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1994 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1995 }
1997 // quit if run by the attr_changed listener
1998 if (g_object_get_data( dataKludge, "freeze" )) {
1999 return;
2000 }
2002 // in turn, prevent listener from responding
2003 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2005 bool modmade = false;
2007 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2008 GSList const *items = selection->itemList();
2009 for (; items != NULL; items = items->next) {
2010 if (SP_IS_STAR((SPItem *) items->data)) {
2011 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2012 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2013 SP_OBJECT(items->data)->updateRepr();
2014 modmade = true;
2015 }
2016 }
2017 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2018 _("Star: Change randomization"));
2020 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2021 }
2024 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2025 gchar const */*old_value*/, gchar const */*new_value*/,
2026 bool /*is_interactive*/, gpointer data)
2027 {
2028 GtkWidget *tbl = GTK_WIDGET(data);
2030 // quit if run by the _changed callbacks
2031 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2032 return;
2033 }
2035 // in turn, prevent callbacks from responding
2036 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2038 GtkAdjustment *adj = 0;
2040 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2041 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2043 if (!strcmp(name, "inkscape:randomized")) {
2044 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2045 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2046 } else if (!strcmp(name, "inkscape:rounded")) {
2047 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2048 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2049 } else if (!strcmp(name, "inkscape:flatsided")) {
2050 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2051 char const *flatsides = repr->attribute("inkscape:flatsided");
2052 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2053 if ( flatsides && !strcmp(flatsides,"false") ) {
2054 ege_select_one_action_set_active( flat_action, 1 );
2055 gtk_action_set_sensitive( prop_action, TRUE );
2056 } else {
2057 ege_select_one_action_set_active( flat_action, 0 );
2058 gtk_action_set_sensitive( prop_action, FALSE );
2059 }
2060 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2061 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2062 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2063 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2064 if (r2 < r1) {
2065 gtk_adjustment_set_value(adj, r2/r1);
2066 } else {
2067 gtk_adjustment_set_value(adj, r1/r2);
2068 }
2069 } else if (!strcmp(name, "sodipodi:sides")) {
2070 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2071 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2072 }
2074 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2075 }
2078 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2079 {
2080 NULL, /* child_added */
2081 NULL, /* child_removed */
2082 star_tb_event_attr_changed,
2083 NULL, /* content_changed */
2084 NULL /* order_changed */
2085 };
2088 /**
2089 * \param selection Should not be NULL.
2090 */
2091 static void
2092 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2093 {
2094 int n_selected = 0;
2095 Inkscape::XML::Node *repr = NULL;
2097 purge_repr_listener( tbl, tbl );
2099 for (GSList const *items = selection->itemList();
2100 items != NULL;
2101 items = items->next)
2102 {
2103 if (SP_IS_STAR((SPItem *) items->data)) {
2104 n_selected++;
2105 repr = SP_OBJECT_REPR((SPItem *) items->data);
2106 }
2107 }
2109 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2111 if (n_selected == 0) {
2112 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2113 } else if (n_selected == 1) {
2114 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2116 if (repr) {
2117 g_object_set_data( tbl, "repr", repr );
2118 Inkscape::GC::anchor(repr);
2119 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2120 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2121 }
2122 } else {
2123 // FIXME: implement averaging of all parameters for multiple selected stars
2124 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2125 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2126 }
2127 }
2130 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2131 {
2132 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2133 // callbacks to lump all the changes for all selected objects in one undo step
2135 GtkAdjustment *adj = 0;
2137 // fixme: make settable in prefs!
2138 gint mag = 5;
2139 gdouble prop = 0.5;
2140 gboolean flat = FALSE;
2141 gdouble randomized = 0;
2142 gdouble rounded = 0;
2144 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2145 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2147 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2148 gtk_action_set_sensitive( sb2, !flat );
2150 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2151 gtk_adjustment_set_value(adj, mag);
2152 gtk_adjustment_value_changed(adj);
2154 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2155 gtk_adjustment_set_value(adj, prop);
2156 gtk_adjustment_value_changed(adj);
2158 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2159 gtk_adjustment_set_value(adj, rounded);
2160 gtk_adjustment_value_changed(adj);
2162 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2163 gtk_adjustment_set_value(adj, randomized);
2164 gtk_adjustment_value_changed(adj);
2165 }
2168 void
2169 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2170 {
2171 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2172 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2173 GtkWidget *l = gtk_label_new(NULL);
2174 gtk_label_set_markup(GTK_LABEL(l), title);
2175 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2176 if ( GTK_IS_TOOLBAR(tbl) ) {
2177 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2178 } else {
2179 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2180 }
2181 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2182 }
2185 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2186 {
2187 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2189 {
2190 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2191 ege_output_action_set_use_markup( act, TRUE );
2192 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2193 g_object_set_data( holder, "mode_action", act );
2194 }
2196 {
2197 EgeAdjustmentAction* eact = 0;
2198 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2199 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2201 /* Flatsided checkbox */
2202 {
2203 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2205 GtkTreeIter iter;
2206 gtk_list_store_append( model, &iter );
2207 gtk_list_store_set( model, &iter,
2208 0, _("Polygon"),
2209 1, _("Regular polygon (with one handle) instead of a star"),
2210 2, "star_flat",
2211 -1 );
2213 gtk_list_store_append( model, &iter );
2214 gtk_list_store_set( model, &iter,
2215 0, _("Star"),
2216 1, _("Star instead of a regular polygon (with one handle)"),
2217 2, "star_angled",
2218 -1 );
2220 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2221 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2222 g_object_set_data( holder, "flat_action", act );
2224 ege_select_one_action_set_appearance( act, "full" );
2225 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2226 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2227 ege_select_one_action_set_icon_column( act, 2 );
2228 ege_select_one_action_set_icon_size( act, secondarySize );
2229 ege_select_one_action_set_tooltip_column( act, 1 );
2231 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2232 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2233 }
2235 /* Magnitude */
2236 {
2237 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2238 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2239 eact = create_adjustment_action( "MagnitudeAction",
2240 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2241 "tools.shapes.star", "magnitude", 3,
2242 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2243 3, 1024, 1, 5,
2244 labels, values, G_N_ELEMENTS(labels),
2245 sp_stb_magnitude_value_changed,
2246 1.0, 0 );
2247 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2248 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2249 }
2251 /* Spoke ratio */
2252 {
2253 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2254 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2255 eact = create_adjustment_action( "SpokeAction",
2256 _("Spoke ratio"), _("Spoke ratio:"),
2257 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2258 // Base radius is the same for the closest handle.
2259 _("Base radius to tip radius ratio"),
2260 "tools.shapes.star", "proportion", 0.5,
2261 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2262 0.01, 1.0, 0.01, 0.1,
2263 labels, values, G_N_ELEMENTS(labels),
2264 sp_stb_proportion_value_changed );
2265 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2266 g_object_set_data( holder, "prop_action", eact );
2267 }
2269 if ( !isFlatSided ) {
2270 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2271 } else {
2272 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2273 }
2275 /* Roundedness */
2276 {
2277 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2278 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2279 eact = create_adjustment_action( "RoundednessAction",
2280 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2281 "tools.shapes.star", "rounded", 0.0,
2282 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2283 -10.0, 10.0, 0.01, 0.1,
2284 labels, values, G_N_ELEMENTS(labels),
2285 sp_stb_rounded_value_changed );
2286 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2287 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2288 }
2290 /* Randomization */
2291 {
2292 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2293 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2294 eact = create_adjustment_action( "RandomizationAction",
2295 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2296 "tools.shapes.star", "randomized", 0.0,
2297 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2298 -10.0, 10.0, 0.001, 0.01,
2299 labels, values, G_N_ELEMENTS(labels),
2300 sp_stb_randomized_value_changed, 0.1, 3 );
2301 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2302 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2303 }
2304 }
2306 {
2307 /* Reset */
2308 {
2309 GtkAction* act = gtk_action_new( "StarResetAction",
2310 _("Defaults"),
2311 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2312 GTK_STOCK_CLEAR );
2313 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2314 gtk_action_group_add_action( mainActions, act );
2315 gtk_action_set_sensitive( act, TRUE );
2316 }
2317 }
2319 sigc::connection *connection = new sigc::connection(
2320 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2321 );
2322 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2323 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2324 }
2327 //########################
2328 //## Rect ##
2329 //########################
2331 static void sp_rtb_sensitivize( GObject *tbl )
2332 {
2333 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2334 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2335 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2337 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2338 gtk_action_set_sensitive( not_rounded, FALSE );
2339 } else {
2340 gtk_action_set_sensitive( not_rounded, TRUE );
2341 }
2342 }
2345 static void
2346 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2347 void (*setter)(SPRect *, gdouble))
2348 {
2349 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2351 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2352 SPUnit const *unit = tracker->getActiveUnit();
2354 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2355 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2356 }
2358 // quit if run by the attr_changed listener
2359 if (g_object_get_data( tbl, "freeze" )) {
2360 return;
2361 }
2363 // in turn, prevent listener from responding
2364 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2366 bool modmade = false;
2367 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2368 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2369 if (SP_IS_RECT(items->data)) {
2370 if (adj->value != 0) {
2371 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2372 } else {
2373 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2374 }
2375 modmade = true;
2376 }
2377 }
2379 sp_rtb_sensitivize( tbl );
2381 if (modmade) {
2382 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2383 _("Change rectangle"));
2384 }
2386 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2387 }
2389 static void
2390 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2391 {
2392 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2393 }
2395 static void
2396 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2397 {
2398 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2399 }
2401 static void
2402 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2403 {
2404 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2405 }
2407 static void
2408 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2409 {
2410 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2411 }
2415 static void
2416 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2417 {
2418 GtkAdjustment *adj = 0;
2420 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2421 gtk_adjustment_set_value(adj, 0.0);
2422 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2423 gtk_adjustment_value_changed(adj);
2425 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2426 gtk_adjustment_set_value(adj, 0.0);
2427 gtk_adjustment_value_changed(adj);
2429 sp_rtb_sensitivize( obj );
2430 }
2432 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2433 gchar const */*old_value*/, gchar const */*new_value*/,
2434 bool /*is_interactive*/, gpointer data)
2435 {
2436 GObject *tbl = G_OBJECT(data);
2438 // quit if run by the _changed callbacks
2439 if (g_object_get_data( tbl, "freeze" )) {
2440 return;
2441 }
2443 // in turn, prevent callbacks from responding
2444 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2446 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2447 SPUnit const *unit = tracker->getActiveUnit();
2449 gpointer item = g_object_get_data( tbl, "item" );
2450 if (item && SP_IS_RECT(item)) {
2451 {
2452 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2453 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2454 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2455 }
2457 {
2458 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2459 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2460 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2461 }
2463 {
2464 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2465 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2466 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2467 }
2469 {
2470 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2471 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2472 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2473 }
2474 }
2476 sp_rtb_sensitivize( tbl );
2478 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2479 }
2482 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2483 NULL, /* child_added */
2484 NULL, /* child_removed */
2485 rect_tb_event_attr_changed,
2486 NULL, /* content_changed */
2487 NULL /* order_changed */
2488 };
2490 /**
2491 * \param selection should not be NULL.
2492 */
2493 static void
2494 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2495 {
2496 int n_selected = 0;
2497 Inkscape::XML::Node *repr = NULL;
2498 SPItem *item = NULL;
2500 if ( g_object_get_data( tbl, "repr" ) ) {
2501 g_object_set_data( tbl, "item", NULL );
2502 }
2503 purge_repr_listener( tbl, tbl );
2505 for (GSList const *items = selection->itemList();
2506 items != NULL;
2507 items = items->next) {
2508 if (SP_IS_RECT((SPItem *) items->data)) {
2509 n_selected++;
2510 item = (SPItem *) items->data;
2511 repr = SP_OBJECT_REPR(item);
2512 }
2513 }
2515 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2517 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2519 if (n_selected == 0) {
2520 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2522 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2523 gtk_action_set_sensitive(w, FALSE);
2524 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2525 gtk_action_set_sensitive(h, FALSE);
2527 } else if (n_selected == 1) {
2528 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2529 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2531 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2532 gtk_action_set_sensitive(w, TRUE);
2533 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2534 gtk_action_set_sensitive(h, TRUE);
2536 if (repr) {
2537 g_object_set_data( tbl, "repr", repr );
2538 g_object_set_data( tbl, "item", item );
2539 Inkscape::GC::anchor(repr);
2540 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2541 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2542 }
2543 } else {
2544 // FIXME: implement averaging of all parameters for multiple selected
2545 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2546 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2547 sp_rtb_sensitivize( tbl );
2548 }
2549 }
2552 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2553 {
2554 EgeAdjustmentAction* eact = 0;
2555 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2557 {
2558 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2559 ege_output_action_set_use_markup( act, TRUE );
2560 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2561 g_object_set_data( holder, "mode_action", act );
2562 }
2564 // rx/ry units menu: create
2565 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2566 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2567 // fixme: add % meaning per cent of the width/height
2568 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2569 g_object_set_data( holder, "tracker", tracker );
2571 /* W */
2572 {
2573 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2574 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2575 eact = create_adjustment_action( "RectWidthAction",
2576 _("Width"), _("W:"), _("Width of rectangle"),
2577 "tools.shapes.rect", "width", 0,
2578 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2579 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2580 labels, values, G_N_ELEMENTS(labels),
2581 sp_rtb_width_value_changed );
2582 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2583 g_object_set_data( holder, "width_action", eact );
2584 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2585 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2586 }
2588 /* H */
2589 {
2590 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2591 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2592 eact = create_adjustment_action( "RectHeightAction",
2593 _("Height"), _("H:"), _("Height of rectangle"),
2594 "tools.shapes.rect", "height", 0,
2595 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2596 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2597 labels, values, G_N_ELEMENTS(labels),
2598 sp_rtb_height_value_changed );
2599 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2600 g_object_set_data( holder, "height_action", eact );
2601 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2602 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2603 }
2605 /* rx */
2606 {
2607 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2608 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2609 eact = create_adjustment_action( "RadiusXAction",
2610 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2611 "tools.shapes.rect", "rx", 0,
2612 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2613 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2614 labels, values, G_N_ELEMENTS(labels),
2615 sp_rtb_rx_value_changed);
2616 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2617 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2618 }
2620 /* ry */
2621 {
2622 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2623 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2624 eact = create_adjustment_action( "RadiusYAction",
2625 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2626 "tools.shapes.rect", "ry", 0,
2627 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2628 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2629 labels, values, G_N_ELEMENTS(labels),
2630 sp_rtb_ry_value_changed);
2631 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2632 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2633 }
2635 // add the units menu
2636 {
2637 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2638 gtk_action_group_add_action( mainActions, act );
2639 }
2641 /* Reset */
2642 {
2643 InkAction* inky = ink_action_new( "RectResetAction",
2644 _("Not rounded"),
2645 _("Make corners sharp"),
2646 "squared_corner",
2647 secondarySize );
2648 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2649 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2650 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2651 g_object_set_data( holder, "not_rounded", inky );
2652 }
2654 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2655 sp_rtb_sensitivize( holder );
2657 sigc::connection *connection = new sigc::connection(
2658 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2659 );
2660 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2661 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2662 }
2664 //########################
2665 //## 3D Box ##
2666 //########################
2668 // normalize angle so that it lies in the interval [0,360]
2669 static double box3d_normalize_angle (double a) {
2670 double angle = a + ((int) (a/360.0))*360;
2671 if (angle < 0) {
2672 angle += 360.0;
2673 }
2674 return angle;
2675 }
2677 static void
2678 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2679 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2680 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2681 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2682 // are reset).
2683 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2685 if (is_infinite) {
2686 gtk_toggle_action_set_active(tact, TRUE);
2687 gtk_action_set_sensitive(act, TRUE);
2689 double angle = persp3d_get_infinite_angle(persp, axis);
2690 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2691 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2692 }
2693 } else {
2694 gtk_toggle_action_set_active(tact, FALSE);
2695 gtk_action_set_sensitive(act, FALSE);
2696 }
2697 }
2699 static void
2700 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2701 if (!persp_repr) {
2702 g_print ("No perspective given to box3d_resync_toolbar().\n");
2703 return;
2704 }
2706 GtkWidget *tbl = GTK_WIDGET(data);
2707 GtkAdjustment *adj = 0;
2708 GtkAction *act = 0;
2709 GtkToggleAction *tact = 0;
2710 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2711 {
2712 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2713 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2714 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2716 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2717 }
2718 {
2719 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2720 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2721 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2723 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2724 }
2725 {
2726 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2727 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2728 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2730 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2731 }
2732 }
2734 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2735 gchar const */*old_value*/, gchar const */*new_value*/,
2736 bool /*is_interactive*/, gpointer data)
2737 {
2738 GtkWidget *tbl = GTK_WIDGET(data);
2740 // quit if run by the attr_changed listener
2741 // note: it used to work without the differently called freeze_ attributes (here and in
2742 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2743 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2744 return;
2745 }
2747 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2748 // sp_document_maybe_done() when the document is undo insensitive)
2749 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2751 // TODO: Only update the appropriate part of the toolbar
2752 // if (!strcmp(name, "inkscape:vp_z")) {
2753 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2754 // }
2756 Persp3D *persp = persp3d_get_from_repr(repr);
2757 persp3d_update_box_reprs(persp);
2759 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2760 }
2762 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2763 {
2764 NULL, /* child_added */
2765 NULL, /* child_removed */
2766 box3d_persp_tb_event_attr_changed,
2767 NULL, /* content_changed */
2768 NULL /* order_changed */
2769 };
2771 /**
2772 * \param selection Should not be NULL.
2773 */
2774 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2775 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2776 static void
2777 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2778 {
2779 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2780 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2781 // update the perspectives with infinite VPs and leave the other ones untouched).
2783 Inkscape::XML::Node *persp_repr = NULL;
2784 purge_repr_listener(tbl, tbl);
2786 SPItem *item = selection->singleItem();
2787 if (item && SP_IS_BOX3D(item)) {
2788 // FIXME: Also deal with multiple selected boxes
2789 SPBox3D *box = SP_BOX3D(item);
2790 Persp3D *persp = box3d_get_perspective(box);
2791 persp_repr = SP_OBJECT_REPR(persp);
2792 if (persp_repr) {
2793 g_object_set_data(tbl, "repr", persp_repr);
2794 Inkscape::GC::anchor(persp_repr);
2795 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2796 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2797 }
2799 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2800 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2802 box3d_resync_toolbar(persp_repr, tbl);
2803 }
2804 }
2806 static void
2807 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2808 {
2809 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2810 SPDocument *document = sp_desktop_document(desktop);
2812 // quit if run by the attr_changed listener
2813 // note: it used to work without the differently called freeze_ attributes (here and in
2814 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2815 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2816 return;
2817 }
2819 // in turn, prevent listener from responding
2820 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2822 //Persp3D *persp = document->current_persp3d;
2823 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2824 if (sel_persps.empty()) {
2825 // this can happen when the document is created; we silently ignore it
2826 return;
2827 }
2828 Persp3D *persp = sel_persps.front();
2830 persp->tmat.set_infinite_direction (axis, adj->value);
2831 SP_OBJECT(persp)->updateRepr();
2833 // TODO: use the correct axis here, too
2834 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2836 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2837 }
2840 static void
2841 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2842 {
2843 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2844 }
2846 static void
2847 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2848 {
2849 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2850 }
2852 static void
2853 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2854 {
2855 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2856 }
2859 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2860 {
2861 // TODO: Take all selected perspectives into account
2862 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2863 if (sel_persps.empty()) {
2864 // this can happen when the document is created; we silently ignore it
2865 return;
2866 }
2867 Persp3D *persp = sel_persps.front();
2869 bool set_infinite = gtk_toggle_action_get_active(act);
2870 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2871 }
2873 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2874 {
2875 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2876 }
2878 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2879 {
2880 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2881 }
2883 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2884 {
2885 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2886 }
2888 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2889 {
2890 EgeAdjustmentAction* eact = 0;
2891 SPDocument *document = sp_desktop_document (desktop);
2892 Persp3D *persp = document->current_persp3d;
2894 EgeAdjustmentAction* box3d_angle_x = 0;
2895 EgeAdjustmentAction* box3d_angle_y = 0;
2896 EgeAdjustmentAction* box3d_angle_z = 0;
2898 /* Angle X */
2899 {
2900 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2901 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2902 eact = create_adjustment_action( "3DBoxAngleXAction",
2903 _("Angle in X direction"), _("Angle X:"),
2904 // Translators: PL is short for 'perspective line'
2905 _("Angle of PLs in X direction"),
2906 "tools.shapes.3dbox", "box3d_angle_x", 30,
2907 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2908 -360.0, 360.0, 1.0, 10.0,
2909 labels, values, G_N_ELEMENTS(labels),
2910 box3d_angle_x_value_changed );
2911 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2912 g_object_set_data( holder, "box3d_angle_x_action", eact );
2913 box3d_angle_x = eact;
2914 }
2916 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2917 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2918 } else {
2919 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2920 }
2923 /* VP X state */
2924 {
2925 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2926 // Translators: VP is short for 'vanishing point'
2927 _("State of VP in X direction"),
2928 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2929 "toggle_vp_x",
2930 Inkscape::ICON_SIZE_DECORATION );
2931 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2932 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2933 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2934 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2935 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2936 }
2938 /* Angle Y */
2939 {
2940 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2941 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2942 eact = create_adjustment_action( "3DBoxAngleYAction",
2943 _("Angle in Y direction"), _("Angle Y:"),
2944 // Translators: PL is short for 'perspective line'
2945 _("Angle of PLs in Y direction"),
2946 "tools.shapes.3dbox", "box3d_angle_y", 30,
2947 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2948 -360.0, 360.0, 1.0, 10.0,
2949 labels, values, G_N_ELEMENTS(labels),
2950 box3d_angle_y_value_changed );
2951 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2952 g_object_set_data( holder, "box3d_angle_y_action", eact );
2953 box3d_angle_y = eact;
2954 }
2956 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2957 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2958 } else {
2959 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2960 }
2962 /* VP Y state */
2963 {
2964 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2965 // Translators: VP is short for 'vanishing point'
2966 _("State of VP in Y direction"),
2967 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2968 "toggle_vp_y",
2969 Inkscape::ICON_SIZE_DECORATION );
2970 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2971 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2972 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2973 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2974 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2975 }
2977 /* Angle Z */
2978 {
2979 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2980 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2981 eact = create_adjustment_action( "3DBoxAngleZAction",
2982 _("Angle in Z direction"), _("Angle Z:"),
2983 // Translators: PL is short for 'perspective line'
2984 _("Angle of PLs in Z direction"),
2985 "tools.shapes.3dbox", "box3d_angle_z", 30,
2986 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2987 -360.0, 360.0, 1.0, 10.0,
2988 labels, values, G_N_ELEMENTS(labels),
2989 box3d_angle_z_value_changed );
2990 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2991 g_object_set_data( holder, "box3d_angle_z_action", eact );
2992 box3d_angle_z = eact;
2993 }
2995 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2996 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2997 } else {
2998 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2999 }
3001 /* VP Z state */
3002 {
3003 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3004 // Translators: VP is short for 'vanishing point'
3005 _("State of VP in Z direction"),
3006 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3007 "toggle_vp_z",
3008 Inkscape::ICON_SIZE_DECORATION );
3009 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3010 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3011 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3012 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3013 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3014 }
3016 sigc::connection *connection = new sigc::connection(
3017 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3018 );
3019 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3020 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3021 }
3023 //########################
3024 //## Spiral ##
3025 //########################
3027 static void
3028 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3029 {
3030 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3032 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3033 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3034 }
3036 // quit if run by the attr_changed listener
3037 if (g_object_get_data( tbl, "freeze" )) {
3038 return;
3039 }
3041 // in turn, prevent listener from responding
3042 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3044 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3046 bool modmade = false;
3047 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3048 items != NULL;
3049 items = items->next)
3050 {
3051 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3052 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3053 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3054 SP_OBJECT((SPItem *) items->data)->updateRepr();
3055 modmade = true;
3056 }
3057 }
3059 g_free(namespaced_name);
3061 if (modmade) {
3062 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3063 _("Change spiral"));
3064 }
3066 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3067 }
3069 static void
3070 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3071 {
3072 sp_spl_tb_value_changed(adj, tbl, "revolution");
3073 }
3075 static void
3076 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3077 {
3078 sp_spl_tb_value_changed(adj, tbl, "expansion");
3079 }
3081 static void
3082 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3083 {
3084 sp_spl_tb_value_changed(adj, tbl, "t0");
3085 }
3087 static void
3088 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3089 {
3090 GtkWidget *tbl = GTK_WIDGET(obj);
3092 GtkAdjustment *adj;
3094 // fixme: make settable
3095 gdouble rev = 5;
3096 gdouble exp = 1.0;
3097 gdouble t0 = 0.0;
3099 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3100 gtk_adjustment_set_value(adj, rev);
3101 gtk_adjustment_value_changed(adj);
3103 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3104 gtk_adjustment_set_value(adj, exp);
3105 gtk_adjustment_value_changed(adj);
3107 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3108 gtk_adjustment_set_value(adj, t0);
3109 gtk_adjustment_value_changed(adj);
3111 spinbutton_defocus(GTK_OBJECT(tbl));
3112 }
3115 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3116 gchar const */*old_value*/, gchar const */*new_value*/,
3117 bool /*is_interactive*/, gpointer data)
3118 {
3119 GtkWidget *tbl = GTK_WIDGET(data);
3121 // quit if run by the _changed callbacks
3122 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3123 return;
3124 }
3126 // in turn, prevent callbacks from responding
3127 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3129 GtkAdjustment *adj;
3130 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3131 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3133 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3134 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3136 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3137 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3139 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3140 }
3143 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3144 NULL, /* child_added */
3145 NULL, /* child_removed */
3146 spiral_tb_event_attr_changed,
3147 NULL, /* content_changed */
3148 NULL /* order_changed */
3149 };
3151 static void
3152 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3153 {
3154 int n_selected = 0;
3155 Inkscape::XML::Node *repr = NULL;
3157 purge_repr_listener( tbl, tbl );
3159 for (GSList const *items = selection->itemList();
3160 items != NULL;
3161 items = items->next)
3162 {
3163 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3164 n_selected++;
3165 repr = SP_OBJECT_REPR((SPItem *) items->data);
3166 }
3167 }
3169 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3171 if (n_selected == 0) {
3172 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3173 } else if (n_selected == 1) {
3174 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3176 if (repr) {
3177 g_object_set_data( tbl, "repr", repr );
3178 Inkscape::GC::anchor(repr);
3179 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3180 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3181 }
3182 } else {
3183 // FIXME: implement averaging of all parameters for multiple selected
3184 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3185 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3186 }
3187 }
3190 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3191 {
3192 EgeAdjustmentAction* eact = 0;
3193 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3195 {
3196 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3197 ege_output_action_set_use_markup( act, TRUE );
3198 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3199 g_object_set_data( holder, "mode_action", act );
3200 }
3202 /* Revolution */
3203 {
3204 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3205 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3206 eact = create_adjustment_action( "SpiralRevolutionAction",
3207 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3208 "tools.shapes.spiral", "revolution", 3.0,
3209 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3210 0.01, 1024.0, 0.1, 1.0,
3211 labels, values, G_N_ELEMENTS(labels),
3212 sp_spl_tb_revolution_value_changed, 1, 2);
3213 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3214 }
3216 /* Expansion */
3217 {
3218 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3219 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3220 eact = create_adjustment_action( "SpiralExpansionAction",
3221 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3222 "tools.shapes.spiral", "expansion", 1.0,
3223 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3224 0.0, 1000.0, 0.01, 1.0,
3225 labels, values, G_N_ELEMENTS(labels),
3226 sp_spl_tb_expansion_value_changed);
3227 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3228 }
3230 /* T0 */
3231 {
3232 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3233 gdouble values[] = {0, 0.5, 0.9};
3234 eact = create_adjustment_action( "SpiralT0Action",
3235 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3236 "tools.shapes.spiral", "t0", 0.0,
3237 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3238 0.0, 0.999, 0.01, 1.0,
3239 labels, values, G_N_ELEMENTS(labels),
3240 sp_spl_tb_t0_value_changed);
3241 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3242 }
3244 /* Reset */
3245 {
3246 InkAction* inky = ink_action_new( "SpiralResetAction",
3247 _("Defaults"),
3248 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3249 GTK_STOCK_CLEAR,
3250 secondarySize );
3251 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3252 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3253 }
3256 sigc::connection *connection = new sigc::connection(
3257 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3258 );
3259 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3260 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3261 }
3263 //########################
3264 //## Pen/Pencil ##
3265 //########################
3267 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3268 {
3269 prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3270 }
3272 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3273 {
3274 // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3275 // adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3276 {
3277 EgeOutputAction* act = ege_output_action_new(
3278 tool_is_pencil ?
3279 "FreehandModeActionPencilTemp" :
3280 "FreehandModeActionPenTemp",
3281 _("<b>Mode:</b>"), "", 0 );
3282 ege_output_action_set_use_markup( act, TRUE );
3283 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3284 g_object_set_data( holder, "freehand_mode_action", act );
3285 }
3287 /* Freehand mode toggle buttons */
3288 {
3289 //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3290 //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3291 guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3292 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3294 {
3295 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3297 GtkTreeIter iter;
3298 gtk_list_store_append( model, &iter );
3299 gtk_list_store_set( model, &iter,
3300 0, _("Bézier"),
3301 1, _("Regular Bézier mode"),
3302 2, "bezier_mode",
3303 -1 );
3305 gtk_list_store_append( model, &iter );
3306 gtk_list_store_set( model, &iter,
3307 0, _("Spiro"),
3308 1, _("Spiro splines mode"),
3309 2, "spiro_splines_mode",
3310 -1 );
3312 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3313 "FreehandModeActionPencil" :
3314 "FreehandModeActionPen",
3315 (""), (""), NULL, GTK_TREE_MODEL(model) );
3316 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3317 g_object_set_data( holder, "freehande_mode_action", act );
3319 ege_select_one_action_set_appearance( act, "full" );
3320 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3321 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3322 ege_select_one_action_set_icon_column( act, 2 );
3323 ege_select_one_action_set_icon_size( act, secondarySize );
3324 ege_select_one_action_set_tooltip_column( act, 1 );
3326 ege_select_one_action_set_active( act, spiroMode);
3327 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3328 }
3329 }
3330 }
3332 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3333 gint shape = ege_select_one_action_get_active( act );
3334 prefs_set_int_attribute("tools.freehand", "shape", shape);
3335 }
3337 /**
3338 * \brief Generate the list of freehand advanced shape option entries.
3339 */
3340 GList * freehand_shape_dropdown_items_list() {
3341 GList *glist = NULL;
3343 glist = g_list_append (glist, _("None"));
3344 glist = g_list_append (glist, _("Clipboard"));
3345 glist = g_list_append (glist, _("Decrescendo"));
3347 return glist;
3348 }
3350 static void
3351 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3352 /*advanced shape options */
3353 {
3354 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3356 GList* items = 0;
3357 gint count = 0;
3358 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3359 {
3360 GtkTreeIter iter;
3361 gtk_list_store_append( model, &iter );
3362 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3363 count++;
3364 }
3365 g_list_free( items );
3366 items = 0;
3367 EgeSelectOneAction* act1 = ege_select_one_action_new(
3368 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3369 _("Shape:"), (""), NULL, GTK_TREE_MODEL(model));
3370 g_object_set( act1, "short_label", _("Shape:"), NULL );
3371 ege_select_one_action_set_appearance( act1, "compact" );
3372 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.freehand", "shape", 0) );
3373 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3374 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3375 g_object_set_data( holder, "shape_action", act1 );
3376 }
3377 }
3379 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3380 {
3381 sp_add_spiro_toggle(mainActions, holder, false);
3382 sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3383 }
3386 static void
3387 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3388 {
3389 GtkWidget *tbl = GTK_WIDGET(obj);
3391 GtkAdjustment *adj;
3393 // fixme: make settable
3394 gdouble tolerance = 4;
3396 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3397 gtk_adjustment_set_value(adj, tolerance);
3398 gtk_adjustment_value_changed(adj);
3400 spinbutton_defocus(GTK_OBJECT(tbl));
3401 }
3403 static void
3404 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3405 {
3407 // quit if run by the attr_changed listener
3408 if (g_object_get_data( tbl, "freeze" )) {
3409 return;
3410 }
3411 // in turn, prevent listener from responding
3412 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3413 prefs_set_double_attribute("tools.freehand.pencil",
3414 "tolerance", adj->value);
3415 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3417 }
3421 static void
3422 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node *repr,
3423 const gchar *key,
3424 const gchar *oldval,
3425 const gchar *newval,
3426 bool is_interactive,
3427 void * data)
3428 {
3429 GObject* tbl = G_OBJECT(data);
3430 if (g_object_get_data( tbl, "freeze" )) {
3431 return;
3432 }
3434 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3436 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3437 "tolerance");
3439 double v = prefs_get_double_attribute("tools.freehand.pencil",
3440 "tolerance", adj->value);
3441 gtk_adjustment_set_value(adj, v);
3442 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3444 }
3446 static Inkscape::XML::NodeEventVector pencil_node_events =
3447 {
3448 NULL,
3449 NULL,
3450 sp_pencil_tb_tolerance_value_changed_external,
3451 NULL,
3452 NULL,
3453 };
3456 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3457 {
3458 sp_add_spiro_toggle(mainActions, holder, true);
3460 EgeAdjustmentAction* eact = 0;
3462 /* Tolerance */
3463 {
3465 eact = create_adjustment_action( "PencilToleranceAction",
3466 _("Number of pixels allowed in interpolating"),
3467 _("Tolerance:"), _("Tolerance"),
3468 "tools.freehand.pencil", "tolerance",
3469 3.0,
3470 GTK_WIDGET(desktop->canvas), NULL,
3471 holder, TRUE, "altx-pencil",
3472 0.5, 100.0, 0.5, 0,
3473 NULL, NULL, 0,
3474 sp_pencil_tb_tolerance_value_changed,
3475 1, 2);
3476 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3477 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3479 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3480 "tools.freehand.pencil");
3481 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3482 g_object_set_data(G_OBJECT(holder), "repr", repr);
3484 }
3486 /* advanced shape options */
3487 sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3489 /* Reset */
3490 {
3491 InkAction* inky = ink_action_new( "PencilResetAction",
3492 _("Defaults"),
3493 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3494 GTK_STOCK_CLEAR,
3495 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3496 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3497 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3498 }
3500 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3502 }
3505 //########################
3506 //## Tweak ##
3507 //########################
3509 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3510 {
3511 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3512 }
3514 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3515 {
3516 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3517 }
3519 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3520 {
3521 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3522 }
3524 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3525 {
3526 int mode = ege_select_one_action_get_active( act );
3527 prefs_set_int_attribute("tools.tweak", "mode", mode);
3529 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3530 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3531 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3532 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3533 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3534 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3535 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3536 if (doh) gtk_action_set_sensitive (doh, TRUE);
3537 if (dos) gtk_action_set_sensitive (dos, TRUE);
3538 if (dol) gtk_action_set_sensitive (dol, TRUE);
3539 if (doo) gtk_action_set_sensitive (doo, TRUE);
3540 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3541 if (fid) gtk_action_set_sensitive (fid, FALSE);
3542 } else {
3543 if (doh) gtk_action_set_sensitive (doh, FALSE);
3544 if (dos) gtk_action_set_sensitive (dos, FALSE);
3545 if (dol) gtk_action_set_sensitive (dol, FALSE);
3546 if (doo) gtk_action_set_sensitive (doo, FALSE);
3547 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3548 if (fid) gtk_action_set_sensitive (fid, TRUE);
3549 }
3550 }
3552 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3553 {
3554 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3555 }
3557 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3558 bool show = gtk_toggle_action_get_active( act );
3559 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3560 }
3561 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3562 bool show = gtk_toggle_action_get_active( act );
3563 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3564 }
3565 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3566 bool show = gtk_toggle_action_get_active( act );
3567 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3568 }
3569 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3570 bool show = gtk_toggle_action_get_active( act );
3571 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3572 }
3574 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3575 {
3576 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3578 {
3579 /* Width */
3580 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3581 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3582 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3583 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3584 "tools.tweak", "width", 15,
3585 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3586 1, 100, 1.0, 0.0,
3587 labels, values, G_N_ELEMENTS(labels),
3588 sp_tweak_width_value_changed, 0.01, 0, 100 );
3589 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3590 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3591 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3592 }
3595 {
3596 /* Force */
3597 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3598 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3599 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3600 _("Force"), _("Force:"), _("The force of the tweak action"),
3601 "tools.tweak", "force", 20,
3602 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3603 1, 100, 1.0, 0.0,
3604 labels, values, G_N_ELEMENTS(labels),
3605 sp_tweak_force_value_changed, 0.01, 0, 100 );
3606 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3607 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3608 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3609 }
3611 /* Mode */
3612 {
3613 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3615 GtkTreeIter iter;
3616 gtk_list_store_append( model, &iter );
3617 gtk_list_store_set( model, &iter,
3618 0, _("Push mode"),
3619 1, _("Push parts of paths in any direction"),
3620 2, "tweak_push_mode",
3621 -1 );
3623 gtk_list_store_append( model, &iter );
3624 gtk_list_store_set( model, &iter,
3625 0, _("Shrink mode"),
3626 1, _("Shrink (inset) parts of paths"),
3627 2, "tweak_shrink_mode",
3628 -1 );
3630 gtk_list_store_append( model, &iter );
3631 gtk_list_store_set( model, &iter,
3632 0, _("Grow mode"),
3633 1, _("Grow (outset) parts of paths"),
3634 2, "tweak_grow_mode",
3635 -1 );
3637 gtk_list_store_append( model, &iter );
3638 gtk_list_store_set( model, &iter,
3639 0, _("Attract mode"),
3640 1, _("Attract parts of paths towards cursor"),
3641 2, "tweak_attract_mode",
3642 -1 );
3644 gtk_list_store_append( model, &iter );
3645 gtk_list_store_set( model, &iter,
3646 0, _("Repel mode"),
3647 1, _("Repel parts of paths from cursor"),
3648 2, "tweak_repel_mode",
3649 -1 );
3651 gtk_list_store_append( model, &iter );
3652 gtk_list_store_set( model, &iter,
3653 0, _("Roughen mode"),
3654 1, _("Roughen parts of paths"),
3655 2, "tweak_roughen_mode",
3656 -1 );
3658 gtk_list_store_append( model, &iter );
3659 gtk_list_store_set( model, &iter,
3660 0, _("Color paint mode"),
3661 1, _("Paint the tool's color upon selected objects"),
3662 2, "tweak_colorpaint_mode",
3663 -1 );
3665 gtk_list_store_append( model, &iter );
3666 gtk_list_store_set( model, &iter,
3667 0, _("Color jitter mode"),
3668 1, _("Jitter the colors of selected objects"),
3669 2, "tweak_colorjitter_mode",
3670 -1 );
3672 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3673 g_object_set( act, "short_label", _("Mode:"), NULL );
3674 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3675 g_object_set_data( holder, "mode_action", act );
3677 ege_select_one_action_set_appearance( act, "full" );
3678 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3679 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3680 ege_select_one_action_set_icon_column( act, 2 );
3681 ege_select_one_action_set_icon_size( act, secondarySize );
3682 ege_select_one_action_set_tooltip_column( act, 1 );
3684 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3685 ege_select_one_action_set_active( act, mode );
3686 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3688 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3689 }
3691 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3693 {
3694 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3695 ege_output_action_set_use_markup( act, TRUE );
3696 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3697 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3698 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3699 g_object_set_data( holder, "tweak_channels_label", act);
3700 }
3702 {
3703 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3704 _("Hue"),
3705 _("In color mode, act on objects' hue"),
3706 NULL,
3707 Inkscape::ICON_SIZE_DECORATION );
3708 //TRANSLATORS: "H" here stands for hue
3709 g_object_set( act, "short_label", _("H"), NULL );
3710 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3711 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3712 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3713 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3714 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3715 g_object_set_data( holder, "tweak_doh", act);
3716 }
3717 {
3718 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3719 _("Saturation"),
3720 _("In color mode, act on objects' saturation"),
3721 NULL,
3722 Inkscape::ICON_SIZE_DECORATION );
3723 //TRANSLATORS: "S" here stands for Saturation
3724 g_object_set( act, "short_label", _("S"), NULL );
3725 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3726 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3727 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3728 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3729 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3730 g_object_set_data( holder, "tweak_dos", act );
3731 }
3732 {
3733 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3734 _("Lightness"),
3735 _("In color mode, act on objects' lightness"),
3736 NULL,
3737 Inkscape::ICON_SIZE_DECORATION );
3738 //TRANSLATORS: "L" here stands for Lightness
3739 g_object_set( act, "short_label", _("L"), NULL );
3740 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3741 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3742 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3743 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3744 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3745 g_object_set_data( holder, "tweak_dol", act );
3746 }
3747 {
3748 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3749 _("Opacity"),
3750 _("In color mode, act on objects' opacity"),
3751 NULL,
3752 Inkscape::ICON_SIZE_DECORATION );
3753 //TRANSLATORS: "O" here stands for Opacity
3754 g_object_set( act, "short_label", _("O"), NULL );
3755 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3756 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3757 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3758 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3759 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3760 g_object_set_data( holder, "tweak_doo", act );
3761 }
3763 { /* Fidelity */
3764 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3765 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3766 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3767 _("Fidelity"), _("Fidelity:"),
3768 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3769 "tools.tweak", "fidelity", 50,
3770 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3771 1, 100, 1.0, 10.0,
3772 labels, values, G_N_ELEMENTS(labels),
3773 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3774 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3775 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3776 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3777 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3778 g_object_set_data( holder, "tweak_fidelity", eact );
3779 }
3782 /* Use Pressure button */
3783 {
3784 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3785 _("Pressure"),
3786 _("Use the pressure of the input device to alter the force of tweak action"),
3787 "use_pressure",
3788 Inkscape::ICON_SIZE_DECORATION );
3789 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3790 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3791 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3792 }
3794 }
3797 //########################
3798 //## Calligraphy ##
3799 //########################
3800 static void update_presets_list(GObject *dataKludge ){
3801 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3802 if (sel) {
3803 ege_select_one_action_set_active(sel, 0 );
3804 }
3805 }
3807 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3808 {
3809 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3810 update_presets_list(tbl);
3811 }
3813 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3814 {
3815 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3816 update_presets_list(tbl);
3817 }
3819 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3820 {
3821 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3822 update_presets_list(tbl);
3823 }
3825 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3826 {
3827 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3828 update_presets_list(tbl);
3829 }
3831 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3832 {
3833 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3834 update_presets_list(tbl);
3835 }
3837 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3838 {
3839 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3840 update_presets_list(tbl);
3841 }
3843 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3844 {
3845 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3846 update_presets_list(tbl);
3847 }
3849 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3850 {
3851 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3852 update_presets_list(tbl);
3853 }
3855 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3856 {
3857 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3858 update_presets_list(tbl);
3859 }
3861 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3862 {
3863 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3864 update_presets_list(tbl);
3865 }
3867 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3868 {
3869 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3870 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3871 update_presets_list(tbl);
3872 if (calligraphy_angle )
3873 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3874 }
3877 #define PROFILE_FLOAT_SIZE 7
3878 #define PROFILE_INT_SIZE 4
3879 struct ProfileFloatElement {
3880 char const *name;
3881 double def;
3882 double min;
3883 double max;
3884 };
3885 struct ProfileIntElement {
3886 char const *name;
3887 int def;
3888 int min;
3889 int max;
3890 };
3894 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3895 {"mass",0.02, 0.0, 1.0},
3896 {"wiggle",0.0, 0.0, 1.0},
3897 {"angle",30.0, -90.0, 90.0},
3898 {"thinning",0.1, -1.0, 1.0},
3899 {"tremor",0.0, 0.0, 1.0},
3900 {"flatness",0.9, 0.0, 1.0},
3901 {"cap_rounding",0.0, 0.0, 5.0}
3902 };
3903 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3904 {"width",15, 1, 100},
3905 {"usepressure",1,0,1},
3906 {"tracebackground",0,0,1},
3907 {"usetilt",1,0,1},
3908 };
3912 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3913 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3914 if (! desktop) return;
3916 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3917 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3918 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3920 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3921 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3922 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3924 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3925 ProfileFloatElement const &pe = f_profile[i];
3926 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3927 prefs_set_double_attribute(pref_path,pe.name,v);
3928 }
3929 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3930 ProfileIntElement const &pe = i_profile[i];
3931 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3932 prefs_set_int_attribute(pref_path,pe.name,v);
3933 }
3934 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3936 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3937 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3938 GtkTreeIter iter;
3939 gtk_list_store_append( model, &iter );
3940 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3942 free(profile_id);
3943 free(pref_path);
3945 ege_select_one_action_set_active(selector, new_index);
3946 }
3949 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3951 gint preset_index = ege_select_one_action_get_active( act );
3952 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3954 if ( profile_name) {
3955 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3956 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3957 ProfileFloatElement const &pe = f_profile[i];
3958 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3959 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3960 if ( adj ) {
3961 gtk_adjustment_set_value(adj, v);
3962 }
3963 }
3964 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3965 ProfileIntElement const &pe = i_profile[i];
3966 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3967 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3968 if ( toggle ) {
3969 gtk_toggle_action_set_active(toggle, v);
3970 } else printf("No toggle");
3971 }
3972 free(profile_name);
3973 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3974 }
3976 }
3979 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3980 {
3981 {
3982 EgeAdjustmentAction* calligraphy_angle = 0;
3984 {
3985 /* Width */
3986 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3987 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3988 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3989 _("Pen Width"), _("Width:"),
3990 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3991 "tools.calligraphic", "width", 15,
3992 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3993 1, 100, 1.0, 0.0,
3994 labels, values, G_N_ELEMENTS(labels),
3995 sp_ddc_width_value_changed, 0.01, 0, 100 );
3996 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3997 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3998 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3999 }
4001 {
4002 /* Thinning */
4003 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4004 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
4005 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4006 _("Stroke Thinning"), _("Thinning:"),
4007 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4008 "tools.calligraphic", "thinning", 0.1,
4009 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4010 -1.0, 1.0, 0.01, 0.1,
4011 labels, values, G_N_ELEMENTS(labels),
4012 sp_ddc_velthin_value_changed, 0.01, 2);
4013 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4014 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4015 }
4017 {
4018 /* Angle */
4019 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4020 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4021 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4022 _("Pen Angle"), _("Angle:"),
4023 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4024 "tools.calligraphic", "angle", 30,
4025 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4026 -90.0, 90.0, 1.0, 10.0,
4027 labels, values, G_N_ELEMENTS(labels),
4028 sp_ddc_angle_value_changed, 1, 0 );
4029 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4030 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4031 g_object_set_data( holder, "angle", eact );
4032 calligraphy_angle = eact;
4033 }
4035 {
4036 /* Fixation */
4037 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4038 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
4039 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4040 _("Fixation"), _("Fixation:"),
4041 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4042 "tools.calligraphic", "flatness", 0.9,
4043 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4044 0.0, 1.0, 0.01, 0.1,
4045 labels, values, G_N_ELEMENTS(labels),
4046 sp_ddc_flatness_value_changed, 0.01, 2 );
4047 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4048 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4049 }
4051 {
4052 /* Cap Rounding */
4053 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4054 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4055 // TRANSLATORS: "cap" means "end" (both start and finish) here
4056 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4057 _("Cap rounding"), _("Caps:"),
4058 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4059 "tools.calligraphic", "cap_rounding", 0.0,
4060 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4061 0.0, 5.0, 0.01, 0.1,
4062 labels, values, G_N_ELEMENTS(labels),
4063 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4064 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4065 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4066 }
4068 {
4069 /* Tremor */
4070 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4071 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
4072 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4073 _("Stroke Tremor"), _("Tremor:"),
4074 _("Increase to make strokes rugged and trembling"),
4075 "tools.calligraphic", "tremor", 0.0,
4076 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4077 0.0, 1.0, 0.01, 0.0,
4078 labels, values, G_N_ELEMENTS(labels),
4079 sp_ddc_tremor_value_changed, 0.01, 2 );
4081 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4082 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4083 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4084 }
4086 {
4087 /* Wiggle */
4088 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4089 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
4090 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4091 _("Pen Wiggle"), _("Wiggle:"),
4092 _("Increase to make the pen waver and wiggle"),
4093 "tools.calligraphic", "wiggle", 0.0,
4094 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4095 0.0, 1.0, 0.01, 0.0,
4096 labels, values, G_N_ELEMENTS(labels),
4097 sp_ddc_wiggle_value_changed, 0.01, 2 );
4098 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4099 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4100 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4101 }
4103 {
4104 /* Mass */
4105 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4106 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
4107 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4108 _("Pen Mass"), _("Mass:"),
4109 _("Increase to make the pen drag behind, as if slowed by inertia"),
4110 "tools.calligraphic", "mass", 0.02,
4111 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4112 0.0, 1.0, 0.01, 0.0,
4113 labels, values, G_N_ELEMENTS(labels),
4114 sp_ddc_mass_value_changed, 0.01, 2 );
4115 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4116 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4117 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4118 }
4121 /* Trace Background button */
4122 {
4123 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4124 _("Trace Background"),
4125 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4126 "trace_background",
4127 Inkscape::ICON_SIZE_DECORATION );
4128 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4129 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4130 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4131 g_object_set_data( holder, "tracebackground", act );
4132 }
4134 /* Use Pressure button */
4135 {
4136 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4137 _("Pressure"),
4138 _("Use the pressure of the input device to alter the width of the pen"),
4139 "use_pressure",
4140 Inkscape::ICON_SIZE_DECORATION );
4141 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4142 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4143 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4144 g_object_set_data( holder, "usepressure", act );
4145 }
4147 /* Use Tilt button */
4148 {
4149 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4150 _("Tilt"),
4151 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4152 "use_tilt",
4153 Inkscape::ICON_SIZE_DECORATION );
4154 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4155 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4156 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4157 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4158 g_object_set_data( holder, "usetilt", act );
4159 }
4161 /*calligraphic profile */
4162 {
4163 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4164 gchar *pref_path;
4167 GtkTreeIter iter;
4168 gtk_list_store_append( model, &iter );
4169 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4171 //TODO: switch back to prefs API
4172 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4173 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4174 int ii=1;
4175 while (child_repr) {
4176 GtkTreeIter iter;
4177 char *preset_name = (char *) child_repr->attribute("name");
4178 gtk_list_store_append( model, &iter );
4179 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4180 child_repr = sp_repr_next(child_repr);
4181 }
4183 pref_path = NULL;
4184 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4185 ege_select_one_action_set_appearance( act1, "compact" );
4186 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4187 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4188 g_object_set_data( holder, "profile_selector", act1 );
4190 }
4192 /*Save or delete calligraphic profile */
4193 {
4194 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4195 _("Defaults"),
4196 _("Save current settings as new profile"),
4197 GTK_STOCK_SAVE );
4198 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4201 gtk_action_group_add_action( mainActions, act );
4202 gtk_action_set_sensitive( act, TRUE );
4203 g_object_set_data( holder, "profile_save_delete", act );
4204 }
4205 }
4206 }
4209 //########################
4210 //## Circle / Arc ##
4211 //########################
4213 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4214 {
4215 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4216 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4218 if (v1 == 0 && v2 == 0) {
4219 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4220 gtk_action_set_sensitive( ocb, FALSE );
4221 gtk_action_set_sensitive( make_whole, FALSE );
4222 }
4223 } else {
4224 gtk_action_set_sensitive( ocb, TRUE );
4225 gtk_action_set_sensitive( make_whole, TRUE );
4226 }
4227 }
4229 static void
4230 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4231 {
4232 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4234 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4235 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4236 }
4238 // quit if run by the attr_changed listener
4239 if (g_object_get_data( tbl, "freeze" )) {
4240 return;
4241 }
4243 // in turn, prevent listener from responding
4244 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4246 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4248 bool modmade = false;
4249 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4250 items != NULL;
4251 items = items->next)
4252 {
4253 SPItem *item = SP_ITEM(items->data);
4255 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4257 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4258 SPArc *arc = SP_ARC(item);
4260 if (!strcmp(value_name, "start"))
4261 ge->start = (adj->value * M_PI)/ 180;
4262 else
4263 ge->end = (adj->value * M_PI)/ 180;
4265 sp_genericellipse_normalize(ge);
4266 ((SPObject *)arc)->updateRepr();
4267 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4269 modmade = true;
4270 }
4271 }
4273 g_free(namespaced_name);
4275 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4277 sp_arctb_sensitivize( tbl, adj->value, other->value );
4279 if (modmade) {
4280 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4281 _("Arc: Change start/end"));
4282 }
4284 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4285 }
4288 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4289 {
4290 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4291 }
4293 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4294 {
4295 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4296 }
4299 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4300 {
4301 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4302 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4303 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4304 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4305 }
4307 // only take action if run by the attr_changed listener
4308 if (!g_object_get_data( tbl, "freeze" )) {
4309 // in turn, prevent listener from responding
4310 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4312 if ( eraserMode != 0 ) {
4313 } else {
4314 }
4315 // TODO finish implementation
4317 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4318 }
4319 }
4321 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4322 {
4323 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4324 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4325 if ( ege_select_one_action_get_active( act ) != 0 ) {
4326 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4327 } else {
4328 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4329 }
4330 }
4332 // quit if run by the attr_changed listener
4333 if (g_object_get_data( tbl, "freeze" )) {
4334 return;
4335 }
4337 // in turn, prevent listener from responding
4338 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4340 bool modmade = false;
4342 if ( ege_select_one_action_get_active(act) != 0 ) {
4343 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4344 items != NULL;
4345 items = items->next)
4346 {
4347 if (SP_IS_ARC((SPItem *) items->data)) {
4348 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4349 repr->setAttribute("sodipodi:open", "true");
4350 SP_OBJECT((SPItem *) items->data)->updateRepr();
4351 modmade = true;
4352 }
4353 }
4354 } else {
4355 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4356 items != NULL;
4357 items = items->next)
4358 {
4359 if (SP_IS_ARC((SPItem *) items->data)) {
4360 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4361 repr->setAttribute("sodipodi:open", NULL);
4362 SP_OBJECT((SPItem *) items->data)->updateRepr();
4363 modmade = true;
4364 }
4365 }
4366 }
4368 if (modmade) {
4369 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4370 _("Arc: Change open/closed"));
4371 }
4373 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4374 }
4376 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4377 {
4378 GtkAdjustment *adj;
4379 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4380 gtk_adjustment_set_value(adj, 0.0);
4381 gtk_adjustment_value_changed(adj);
4383 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4384 gtk_adjustment_set_value(adj, 0.0);
4385 gtk_adjustment_value_changed(adj);
4387 spinbutton_defocus( GTK_OBJECT(obj) );
4388 }
4390 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4391 gchar const */*old_value*/, gchar const */*new_value*/,
4392 bool /*is_interactive*/, gpointer data)
4393 {
4394 GObject *tbl = G_OBJECT(data);
4396 // quit if run by the _changed callbacks
4397 if (g_object_get_data( tbl, "freeze" )) {
4398 return;
4399 }
4401 // in turn, prevent callbacks from responding
4402 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4404 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4405 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4407 GtkAdjustment *adj1,*adj2;
4408 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4409 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4410 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4411 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4413 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4415 char const *openstr = NULL;
4416 openstr = repr->attribute("sodipodi:open");
4417 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4419 if (openstr) {
4420 ege_select_one_action_set_active( ocb, 1 );
4421 } else {
4422 ege_select_one_action_set_active( ocb, 0 );
4423 }
4425 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4426 }
4428 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4429 NULL, /* child_added */
4430 NULL, /* child_removed */
4431 arc_tb_event_attr_changed,
4432 NULL, /* content_changed */
4433 NULL /* order_changed */
4434 };
4437 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4438 {
4439 int n_selected = 0;
4440 Inkscape::XML::Node *repr = NULL;
4442 purge_repr_listener( tbl, tbl );
4444 for (GSList const *items = selection->itemList();
4445 items != NULL;
4446 items = items->next)
4447 {
4448 if (SP_IS_ARC((SPItem *) items->data)) {
4449 n_selected++;
4450 repr = SP_OBJECT_REPR((SPItem *) items->data);
4451 }
4452 }
4454 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4456 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4457 if (n_selected == 0) {
4458 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4459 } else if (n_selected == 1) {
4460 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4461 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4463 if (repr) {
4464 g_object_set_data( tbl, "repr", repr );
4465 Inkscape::GC::anchor(repr);
4466 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4467 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4468 }
4469 } else {
4470 // FIXME: implement averaging of all parameters for multiple selected
4471 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4472 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4473 sp_arctb_sensitivize( tbl, 1, 0 );
4474 }
4475 }
4478 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4479 {
4480 EgeAdjustmentAction* eact = 0;
4481 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4484 {
4485 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4486 ege_output_action_set_use_markup( act, TRUE );
4487 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4488 g_object_set_data( holder, "mode_action", act );
4489 }
4491 /* Start */
4492 {
4493 eact = create_adjustment_action( "ArcStartAction",
4494 _("Start"), _("Start:"),
4495 _("The angle (in degrees) from the horizontal to the arc's start point"),
4496 "tools.shapes.arc", "start", 0.0,
4497 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4498 -360.0, 360.0, 1.0, 10.0,
4499 0, 0, 0,
4500 sp_arctb_start_value_changed);
4501 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4502 }
4504 /* End */
4505 {
4506 eact = create_adjustment_action( "ArcEndAction",
4507 _("End"), _("End:"),
4508 _("The angle (in degrees) from the horizontal to the arc's end point"),
4509 "tools.shapes.arc", "end", 0.0,
4510 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4511 -360.0, 360.0, 1.0, 10.0,
4512 0, 0, 0,
4513 sp_arctb_end_value_changed);
4514 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4515 }
4517 /* Segments / Pie checkbox */
4518 {
4519 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4521 GtkTreeIter iter;
4522 gtk_list_store_append( model, &iter );
4523 gtk_list_store_set( model, &iter,
4524 0, _("Closed arc"),
4525 1, _("Switch to segment (closed shape with two radii)"),
4526 2, "circle_closed_arc",
4527 -1 );
4529 gtk_list_store_append( model, &iter );
4530 gtk_list_store_set( model, &iter,
4531 0, _("Open Arc"),
4532 1, _("Switch to arc (unclosed shape)"),
4533 2, "circle_open_arc",
4534 -1 );
4536 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4537 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4538 g_object_set_data( holder, "open_action", act );
4540 ege_select_one_action_set_appearance( act, "full" );
4541 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4542 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4543 ege_select_one_action_set_icon_column( act, 2 );
4544 ege_select_one_action_set_icon_size( act, secondarySize );
4545 ege_select_one_action_set_tooltip_column( act, 1 );
4547 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4548 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4549 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4550 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4551 }
4553 /* Make Whole */
4554 {
4555 InkAction* inky = ink_action_new( "ArcResetAction",
4556 _("Make whole"),
4557 _("Make the shape a whole ellipse, not arc or segment"),
4558 "reset_circle",
4559 secondarySize );
4560 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4561 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4562 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4563 g_object_set_data( holder, "make_whole", inky );
4564 }
4566 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4567 // sensitivize make whole and open checkbox
4568 {
4569 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4570 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4571 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4572 }
4575 sigc::connection *connection = new sigc::connection(
4576 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4577 );
4578 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4579 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4580 }
4585 // toggle button callbacks and updaters
4587 //########################
4588 //## Dropper ##
4589 //########################
4591 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4592 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4593 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4594 if ( set_action ) {
4595 if ( gtk_toggle_action_get_active( act ) ) {
4596 gtk_action_set_sensitive( set_action, TRUE );
4597 } else {
4598 gtk_action_set_sensitive( set_action, FALSE );
4599 }
4600 }
4602 spinbutton_defocus(GTK_OBJECT(tbl));
4603 }
4605 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4606 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4607 spinbutton_defocus(GTK_OBJECT(tbl));
4608 }
4611 /**
4612 * Dropper auxiliary toolbar construction and setup.
4613 *
4614 * TODO: Would like to add swatch of current color.
4615 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4616 * can drag and drop places. Will provide a nice mixing palette.
4617 */
4618 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4619 {
4620 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4622 {
4623 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4624 ege_output_action_set_use_markup( act, TRUE );
4625 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4626 }
4628 {
4629 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4630 _("Pick opacity"),
4631 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4632 NULL,
4633 Inkscape::ICON_SIZE_DECORATION );
4634 g_object_set( act, "short_label", _("Pick"), NULL );
4635 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4636 g_object_set_data( holder, "pick_action", act );
4637 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4638 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4639 }
4641 {
4642 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4643 _("Assign opacity"),
4644 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4645 NULL,
4646 Inkscape::ICON_SIZE_DECORATION );
4647 g_object_set( act, "short_label", _("Assign"), NULL );
4648 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4649 g_object_set_data( holder, "set_action", act );
4650 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4651 // make sure it's disabled if we're not picking alpha
4652 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4653 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4654 }
4655 }
4659 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4660 {
4661 {
4662 /* Width */
4663 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4664 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4665 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4666 _("Pen Width"), _("Width:"),
4667 _("The width of the eraser pen (relative to the visible canvas area)"),
4668 "tools.eraser", "width", 15,
4669 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4670 1, 100, 1.0, 0.0,
4671 labels, values, G_N_ELEMENTS(labels),
4672 sp_ddc_width_value_changed, 0.01, 0, 100 );
4673 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4674 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4675 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4676 }
4678 {
4679 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4681 GtkTreeIter iter;
4682 gtk_list_store_append( model, &iter );
4683 gtk_list_store_set( model, &iter,
4684 0, _("Delete"),
4685 1, _("Delete objects touched by the eraser"),
4686 2, "delete_object",
4687 -1 );
4689 gtk_list_store_append( model, &iter );
4690 gtk_list_store_set( model, &iter,
4691 0, _("Cut"),
4692 1, _("Cut out from objects"),
4693 2, "difference",
4694 -1 );
4696 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4697 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4698 g_object_set_data( holder, "eraser_mode_action", act );
4700 ege_select_one_action_set_appearance( act, "full" );
4701 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4702 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4703 ege_select_one_action_set_icon_column( act, 2 );
4704 ege_select_one_action_set_tooltip_column( act, 1 );
4706 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4707 ege_select_one_action_set_active( act, eraserMode );
4708 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4709 }
4711 }
4713 //########################
4714 //## Text Toolbox ##
4715 //########################
4716 /*
4717 static void
4718 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4719 {
4720 //Call back for letter sizing spinbutton
4721 }
4723 static void
4724 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4725 {
4726 //Call back for line height spinbutton
4727 }
4729 static void
4730 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4731 {
4732 //Call back for horizontal kerning spinbutton
4733 }
4735 static void
4736 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4737 {
4738 //Call back for vertical kerning spinbutton
4739 }
4741 static void
4742 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4743 {
4744 //Call back for letter rotation spinbutton
4745 }*/
4747 namespace {
4749 bool popdown_visible = false;
4750 bool popdown_hasfocus = false;
4752 void
4753 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4754 {
4755 SPStyle *query =
4756 sp_style_new (SP_ACTIVE_DOCUMENT);
4758 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4760 int result_family =
4761 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4763 int result_style =
4764 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4766 int result_numbers =
4767 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4769 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4771 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4772 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4773 {
4774 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4776 if (repr)
4777 {
4778 sp_style_read_from_repr (query, repr);
4779 }
4780 else
4781 {
4782 return;
4783 }
4784 }
4786 if (query->text)
4787 {
4788 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4789 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4790 gtk_entry_set_text (GTK_ENTRY (entry), "");
4792 } else if (query->text->font_specification.value || query->text->font_family.value) {
4794 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4796 // Get the font that corresponds
4797 Glib::ustring familyName;
4799 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4800 if (font) {
4801 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4802 font->Unref();
4803 font = NULL;
4804 }
4806 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4808 Gtk::TreePath path;
4809 try {
4810 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4811 } catch (...) {
4812 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4813 return;
4814 }
4816 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4817 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4819 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4821 gtk_tree_selection_select_path (tselection, path.gobj());
4822 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4824 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4825 }
4827 //Size
4828 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4829 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4830 g_object_set_data (tbl, "size-block", gpointer(1));
4831 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4832 g_object_set_data (tbl, "size-block", gpointer(0));
4833 free (str);
4835 //Anchor
4836 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4837 {
4838 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4839 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4840 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4841 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4842 }
4843 else
4844 {
4845 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4846 {
4847 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4848 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4849 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4850 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4851 }
4852 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4853 {
4854 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4855 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4856 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4857 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4858 }
4859 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4860 {
4861 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4862 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4863 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4864 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4865 }
4866 }
4868 //Style
4869 {
4870 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4872 gboolean active = gtk_toggle_button_get_active (button);
4873 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4875 if (active != check)
4876 {
4877 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4878 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4879 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4880 }
4881 }
4883 {
4884 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4886 gboolean active = gtk_toggle_button_get_active (button);
4887 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4889 if (active != check)
4890 {
4891 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4892 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4893 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4894 }
4895 }
4897 //Orientation
4898 //locking both buttons, changing one affect all group (both)
4899 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4900 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4902 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4903 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4905 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4906 {
4907 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4908 }
4909 else
4910 {
4911 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4912 }
4913 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4914 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4915 }
4917 sp_style_unref(query);
4918 }
4920 void
4921 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4922 {
4923 sp_text_toolbox_selection_changed (selection, tbl);
4924 }
4926 void
4927 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4928 {
4929 sp_text_toolbox_selection_changed (NULL, tbl);
4930 }
4932 void
4933 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4934 GObject *tbl)
4935 {
4936 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4937 GtkTreeModel *model = 0;
4938 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4939 GtkTreeIter iter;
4940 char *family = 0;
4942 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4943 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4945 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4946 return;
4947 }
4949 gtk_tree_model_get (model, &iter, 0, &family, -1);
4951 if (g_object_get_data (G_OBJECT (selection), "block"))
4952 {
4953 gtk_entry_set_text (GTK_ENTRY (entry), family);
4954 return;
4955 }
4957 gtk_entry_set_text (GTK_ENTRY (entry), family);
4959 SPStyle *query =
4960 sp_style_new (SP_ACTIVE_DOCUMENT);
4962 int result_fontspec =
4963 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4965 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4967 SPCSSAttr *css = sp_repr_css_attr_new ();
4970 // First try to get the font spec from the stored value
4971 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4973 if (fontSpec.empty()) {
4974 // Construct a new font specification if it does not yet exist
4975 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4976 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4977 fontFromStyle->Unref();
4978 }
4980 if (!fontSpec.empty()) {
4981 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4982 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4983 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4984 if (font) {
4985 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4987 // Set all the these just in case they were altered when finding the best
4988 // match for the new family and old style...
4990 gchar c[256];
4992 font->Family(c, 256);
4993 sp_repr_css_set_property (css, "font-family", c);
4995 font->Attribute( "weight", c, 256);
4996 sp_repr_css_set_property (css, "font-weight", c);
4998 font->Attribute("style", c, 256);
4999 sp_repr_css_set_property (css, "font-style", c);
5001 font->Attribute("stretch", c, 256);
5002 sp_repr_css_set_property (css, "font-stretch", c);
5004 font->Attribute("variant", c, 256);
5005 sp_repr_css_set_property (css, "font-variant", c);
5007 font->Unref();
5008 }
5009 }
5010 }
5012 // If querying returned nothing, set the default style of the tool (for new texts)
5013 if (result_fontspec == QUERY_STYLE_NOTHING)
5014 {
5015 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5016 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5017 }
5018 else
5019 {
5020 sp_desktop_set_style (desktop, css, true, true);
5021 }
5023 sp_style_unref(query);
5025 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5026 _("Text: Change font family"));
5027 sp_repr_css_attr_unref (css);
5028 free (family);
5029 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5031 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5032 }
5034 void
5035 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5036 GObject *tbl)
5037 {
5038 const char *family = gtk_entry_get_text (entry);
5040 try {
5041 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5042 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5043 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5044 gtk_tree_selection_select_path (selection, path.gobj());
5045 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5046 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5047 } catch (...) {
5048 if (family && strlen (family))
5049 {
5050 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5051 }
5052 }
5053 }
5055 void
5056 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5057 gpointer data)
5058 {
5059 if (g_object_get_data (G_OBJECT (button), "block")) return;
5060 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5061 int prop = GPOINTER_TO_INT(data);
5063 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5064 SPCSSAttr *css = sp_repr_css_attr_new ();
5066 switch (prop)
5067 {
5068 case 0:
5069 {
5070 sp_repr_css_set_property (css, "text-anchor", "start");
5071 sp_repr_css_set_property (css, "text-align", "start");
5072 break;
5073 }
5074 case 1:
5075 {
5076 sp_repr_css_set_property (css, "text-anchor", "middle");
5077 sp_repr_css_set_property (css, "text-align", "center");
5078 break;
5079 }
5081 case 2:
5082 {
5083 sp_repr_css_set_property (css, "text-anchor", "end");
5084 sp_repr_css_set_property (css, "text-align", "end");
5085 break;
5086 }
5088 case 3:
5089 {
5090 sp_repr_css_set_property (css, "text-anchor", "start");
5091 sp_repr_css_set_property (css, "text-align", "justify");
5092 break;
5093 }
5094 }
5096 SPStyle *query =
5097 sp_style_new (SP_ACTIVE_DOCUMENT);
5098 int result_numbers =
5099 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5101 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5102 if (result_numbers == QUERY_STYLE_NOTHING)
5103 {
5104 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5105 }
5107 sp_style_unref(query);
5109 sp_desktop_set_style (desktop, css, true, true);
5110 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5111 _("Text: Change alignment"));
5112 sp_repr_css_attr_unref (css);
5114 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5115 }
5117 void
5118 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5119 gpointer data)
5120 {
5121 if (g_object_get_data (G_OBJECT (button), "block")) return;
5123 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5124 SPCSSAttr *css = sp_repr_css_attr_new ();
5125 int prop = GPOINTER_TO_INT(data);
5126 bool active = gtk_toggle_button_get_active (button);
5128 SPStyle *query =
5129 sp_style_new (SP_ACTIVE_DOCUMENT);
5131 int result_fontspec =
5132 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5134 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5135 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5136 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5138 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5139 Glib::ustring newFontSpec = "";
5141 if (fontSpec.empty()) {
5142 // Construct a new font specification if it does not yet exist
5143 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5144 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5145 fontFromStyle->Unref();
5146 }
5148 switch (prop)
5149 {
5150 case 0:
5151 {
5152 if (!fontSpec.empty()) {
5153 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5154 }
5155 if (fontSpec != newFontSpec) {
5156 // Don't even set the bold if the font didn't exist on the system
5157 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5158 }
5159 break;
5160 }
5162 case 1:
5163 {
5164 if (!fontSpec.empty()) {
5165 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5166 }
5167 if (fontSpec != newFontSpec) {
5168 // Don't even set the italic if the font didn't exist on the system
5169 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5170 }
5171 break;
5172 }
5173 }
5175 if (!newFontSpec.empty()) {
5176 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5177 }
5179 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5180 if (result_fontspec == QUERY_STYLE_NOTHING)
5181 {
5182 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5183 }
5185 sp_style_unref(query);
5187 sp_desktop_set_style (desktop, css, true, true);
5188 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5189 _("Text: Change font style"));
5190 sp_repr_css_attr_unref (css);
5192 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5193 }
5195 void
5196 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5197 gpointer data)
5198 {
5199 if (g_object_get_data (G_OBJECT (button), "block")) {
5200 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5201 return;
5202 }
5204 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5205 SPCSSAttr *css = sp_repr_css_attr_new ();
5206 int prop = GPOINTER_TO_INT(data);
5208 switch (prop)
5209 {
5210 case 0:
5211 {
5212 sp_repr_css_set_property (css, "writing-mode", "lr");
5213 break;
5214 }
5216 case 1:
5217 {
5218 sp_repr_css_set_property (css, "writing-mode", "tb");
5219 break;
5220 }
5221 }
5223 SPStyle *query =
5224 sp_style_new (SP_ACTIVE_DOCUMENT);
5225 int result_numbers =
5226 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5228 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5229 if (result_numbers == QUERY_STYLE_NOTHING)
5230 {
5231 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5232 }
5234 sp_desktop_set_style (desktop, css, true, true);
5235 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5236 _("Text: Change orientation"));
5237 sp_repr_css_attr_unref (css);
5239 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5240 }
5242 gboolean
5243 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5244 {
5245 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5246 if (!desktop) return FALSE;
5248 switch (get_group0_keyval (event)) {
5249 case GDK_Escape: // defocus
5250 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5251 sp_text_toolbox_selection_changed (NULL, tbl); // update
5252 return TRUE; // I consumed the event
5253 break;
5254 }
5255 return FALSE;
5256 }
5258 gboolean
5259 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5260 {
5261 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5262 if (!desktop) return FALSE;
5264 switch (get_group0_keyval (event)) {
5265 case GDK_KP_Enter:
5266 case GDK_Return:
5267 case GDK_Escape: // defocus
5268 gtk_widget_hide (w);
5269 popdown_visible = false;
5270 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5271 return TRUE; // I consumed the event
5272 break;
5273 case GDK_w:
5274 case GDK_W:
5275 if (event->state & GDK_CONTROL_MASK) {
5276 gtk_widget_hide (w);
5277 popdown_visible = false;
5278 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5279 return TRUE; // I consumed the event
5280 }
5281 break;
5282 }
5283 return FALSE;
5284 }
5287 void
5288 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5289 GObject *tbl)
5290 {
5291 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5293 if (g_object_get_data (tbl, "size-block")) return;
5295 // If this is not from selecting a size in the list (in which case get_active will give the
5296 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5297 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5298 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5299 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5300 return;
5302 gchar *endptr;
5303 gdouble value = -1;
5304 char *text = gtk_combo_box_get_active_text (cbox);
5305 if (text) {
5306 value = g_strtod (text, &endptr);
5307 if (endptr == text) // conversion failed, non-numeric input
5308 value = -1;
5309 free (text);
5310 }
5311 if (value <= 0) {
5312 return; // could not parse value
5313 }
5315 SPCSSAttr *css = sp_repr_css_attr_new ();
5316 Inkscape::CSSOStringStream osfs;
5317 osfs << value;
5318 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5320 SPStyle *query =
5321 sp_style_new (SP_ACTIVE_DOCUMENT);
5322 int result_numbers =
5323 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5325 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5326 if (result_numbers == QUERY_STYLE_NOTHING)
5327 {
5328 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5329 }
5331 sp_style_unref(query);
5333 sp_desktop_set_style (desktop, css, true, true);
5334 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5335 _("Text: Change font size"));
5336 sp_repr_css_attr_unref (css);
5338 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5339 }
5341 gboolean
5342 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5343 {
5344 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5345 if (!desktop) return FALSE;
5347 if (!g_object_get_data (tbl, "esc-pressed")) {
5348 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5349 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5350 sp_text_toolbox_size_changed (cbox, tbl);
5351 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5352 }
5353 return FALSE; // I consumed the event
5354 }
5357 gboolean
5358 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5359 {
5360 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5361 if (!desktop) return FALSE;
5363 switch (get_group0_keyval (event)) {
5364 case GDK_Escape: // defocus
5365 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5366 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5367 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5368 return TRUE; // I consumed the event
5369 break;
5370 case GDK_Return: // defocus
5371 case GDK_KP_Enter:
5372 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5373 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5374 sp_text_toolbox_size_changed (cbox, tbl);
5375 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5376 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5377 return TRUE; // I consumed the event
5378 break;
5379 }
5380 return FALSE;
5381 }
5383 void
5384 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5385 GObject *tbl)
5386 {
5387 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5388 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5389 int x, y;
5391 if (!popdown_visible)
5392 {
5393 gdk_window_get_origin (widget->window, &x, &y);
5394 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5395 gtk_widget_show_all (popdown);
5396 //sp_transientize (popdown);
5398 gdk_pointer_grab (widget->window, TRUE,
5399 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5400 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5401 GDK_POINTER_MOTION_MASK),
5402 NULL, NULL, GDK_CURRENT_TIME);
5404 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5406 popdown_visible = true;
5407 }
5408 else
5409 {
5410 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5411 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5412 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5413 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5414 gtk_widget_hide (popdown);
5415 popdown_visible = false;
5416 }
5417 }
5419 gboolean
5420 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5421 GdkEventFocus */*event*/,
5422 GObject */*tbl*/)
5423 {
5424 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5425 return FALSE;
5426 }
5428 gboolean
5429 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5430 GdkEventFocus */*event*/,
5431 GObject */*tbl*/)
5432 {
5433 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5435 if (popdown_hasfocus) {
5436 gtk_widget_hide (popdown);
5437 popdown_hasfocus = false;
5438 popdown_visible = false;
5439 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5440 return TRUE;
5441 }
5442 return FALSE;
5443 }
5445 gboolean
5446 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5447 GdkEventFocus */*event*/,
5448 GObject */*tbl*/)
5449 {
5450 popdown_hasfocus = true;
5451 return TRUE;
5452 }
5455 void
5456 cell_data_func (GtkTreeViewColumn */*column*/,
5457 GtkCellRenderer *cell,
5458 GtkTreeModel *tree_model,
5459 GtkTreeIter *iter,
5460 gpointer /*data*/)
5461 {
5462 char *family,
5463 *family_escaped,
5464 *sample_escaped;
5466 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5468 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5470 family_escaped = g_markup_escape_text (family, -1);
5471 sample_escaped = g_markup_escape_text (sample, -1);
5473 std::stringstream markup;
5474 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5475 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5477 free (family);
5478 free (family_escaped);
5479 free (sample_escaped);
5480 }
5482 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5483 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5484 if (completion) {
5485 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5486 g_object_unref (completion);
5487 }
5488 }
5490 GtkWidget*
5491 sp_text_toolbox_new (SPDesktop *desktop)
5492 {
5493 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5494 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5496 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5497 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5499 GtkTooltips *tt = gtk_tooltips_new();
5500 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5502 ////////////Family
5503 //Window
5504 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5505 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5507 //Entry
5508 GtkWidget *entry = gtk_entry_new ();
5509 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5510 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5511 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5512 gtk_entry_completion_set_text_column (completion, 0);
5513 gtk_entry_completion_set_minimum_key_length (completion, 1);
5514 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5515 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5516 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5517 gtk_toolbar_append_widget( tbl, entry, "", "" );
5518 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5520 //Button
5521 GtkWidget *button = gtk_button_new ();
5522 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5523 gtk_toolbar_append_widget( tbl, button, "", "");
5525 //Popdown
5526 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5527 GtkWidget *treeview = gtk_tree_view_new ();
5529 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5530 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5531 gtk_tree_view_column_pack_start (column, cell, FALSE);
5532 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5533 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5534 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5536 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5537 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5538 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5540 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5542 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5543 gtk_container_add (GTK_CONTAINER (sw), treeview);
5545 gtk_container_add (GTK_CONTAINER (window), sw);
5546 gtk_widget_set_size_request (window, 300, 450);
5548 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5549 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5550 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5552 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5554 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5555 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5556 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5558 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5559 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5561 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5562 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5563 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5564 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5565 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5567 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5568 GtkWidget *box = gtk_event_box_new ();
5569 gtk_container_add (GTK_CONTAINER (box), image);
5570 gtk_toolbar_append_widget( tbl, box, "", "");
5571 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5572 GtkTooltips *tooltips = gtk_tooltips_new ();
5573 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5574 gtk_widget_hide (GTK_WIDGET (box));
5575 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5577 ////////////Size
5578 const char *sizes[] = {
5579 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5580 "16", "18", "20", "22", "24", "28",
5581 "32", "36", "40", "48", "56", "64", "72", "144"
5582 };
5584 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5585 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5586 gtk_widget_set_size_request (cbox, 80, -1);
5587 gtk_toolbar_append_widget( tbl, cbox, "", "");
5588 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5589 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5590 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5591 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5593 ////////////Text anchor
5594 GtkWidget *group = gtk_radio_button_new (NULL);
5595 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5596 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5598 // left
5599 GtkWidget *rbutton = group;
5600 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5601 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5602 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5604 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5605 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5606 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5607 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5609 // center
5610 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5611 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5612 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5613 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5615 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5616 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5617 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5618 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5620 // right
5621 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5622 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5623 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5624 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5626 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5627 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5628 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5629 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5631 // fill
5632 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5633 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5634 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5635 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5637 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5638 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5639 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5640 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5642 gtk_toolbar_append_widget( tbl, row, "", "");
5644 //spacer
5645 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5647 ////////////Text style
5648 row = gtk_hbox_new (FALSE, 4);
5650 // bold
5651 rbutton = gtk_toggle_button_new ();
5652 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5653 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5654 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5655 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5657 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5658 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5659 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5661 // italic
5662 rbutton = gtk_toggle_button_new ();
5663 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5664 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5665 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5666 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5668 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5669 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5670 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5672 gtk_toolbar_append_widget( tbl, row, "", "");
5674 //spacer
5675 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5677 ////////////Text orientation
5678 group = gtk_radio_button_new (NULL);
5679 row = gtk_hbox_new (FALSE, 4);
5680 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5682 // horizontal
5683 rbutton = group;
5684 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5685 gtk_container_add (GTK_CONTAINER (rbutton),
5686 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5687 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5688 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5690 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5691 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5692 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5694 // vertical
5695 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5696 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5697 gtk_container_add (GTK_CONTAINER (rbutton),
5698 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5699 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5700 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5702 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5703 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5704 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5705 gtk_toolbar_append_widget( tbl, row, "", "" );
5708 //watch selection
5709 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5711 sigc::connection *c_selection_changed =
5712 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5713 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5714 pool->add_connection ("selection-changed", c_selection_changed);
5716 sigc::connection *c_selection_modified =
5717 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5718 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5719 pool->add_connection ("selection-modified", c_selection_modified);
5721 sigc::connection *c_subselection_changed =
5722 new sigc::connection (desktop->connectToolSubselectionChanged
5723 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5724 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5726 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5729 gtk_widget_show_all( GTK_WIDGET(tbl) );
5731 return GTK_WIDGET(tbl);
5732 } // end of sp_text_toolbox_new()
5734 }//<unnamed> namespace
5737 //#########################
5738 //## Connector ##
5739 //#########################
5741 static void sp_connector_path_set_avoid(void)
5742 {
5743 cc_selection_set_avoid(true);
5744 }
5747 static void sp_connector_path_set_ignore(void)
5748 {
5749 cc_selection_set_avoid(false);
5750 }
5754 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5755 {
5756 // quit if run by the _changed callbacks
5757 if (g_object_get_data( tbl, "freeze" )) {
5758 return;
5759 }
5761 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5762 SPDocument *doc = sp_desktop_document(desktop);
5764 if (!sp_document_get_undo_sensitive(doc))
5765 {
5766 return;
5767 }
5769 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5771 if ( repr->attribute("inkscape:connector-spacing") ) {
5772 gdouble priorValue = gtk_adjustment_get_value(adj);
5773 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5774 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5775 return;
5776 }
5777 } else if ( adj->value == defaultConnSpacing ) {
5778 return;
5779 }
5781 // in turn, prevent callbacks from responding
5782 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5784 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5785 SP_OBJECT(desktop->namedview)->updateRepr();
5787 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5788 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5789 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5790 NR::Matrix m = NR::identity();
5791 avoid_item_move(&m, item);
5792 }
5794 if (items) {
5795 g_slist_free(items);
5796 }
5798 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5799 _("Change connector spacing"));
5801 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5803 spinbutton_defocus(GTK_OBJECT(tbl));
5804 }
5806 static void sp_connector_graph_layout(void)
5807 {
5808 if (!SP_ACTIVE_DESKTOP) return;
5810 // hack for clones, see comment in align-and-distribute.cpp
5811 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5812 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5814 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5816 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5818 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5819 }
5821 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5822 {
5823 if ( gtk_toggle_action_get_active( act ) ) {
5824 prefs_set_string_attribute("tools.connector", "directedlayout",
5825 "true");
5826 } else {
5827 prefs_set_string_attribute("tools.connector", "directedlayout",
5828 "false");
5829 }
5830 }
5832 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5833 {
5834 if ( gtk_toggle_action_get_active( act ) ) {
5835 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5836 "true");
5837 } else {
5838 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5839 "false");
5840 }
5841 }
5844 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5845 {
5846 prefs_set_double_attribute("tools.connector", "length", adj->value);
5847 spinbutton_defocus(GTK_OBJECT(tbl));
5848 }
5850 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5851 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5852 bool /*is_interactive*/, gpointer data)
5853 {
5854 GtkWidget *tbl = GTK_WIDGET(data);
5856 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5857 return;
5858 }
5859 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5860 return;
5861 }
5863 GtkAdjustment *adj = (GtkAdjustment*)
5864 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5865 gdouble spacing = defaultConnSpacing;
5866 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5868 gtk_adjustment_set_value(adj, spacing);
5869 }
5872 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5873 NULL, /* child_added */
5874 NULL, /* child_removed */
5875 connector_tb_event_attr_changed,
5876 NULL, /* content_changed */
5877 NULL /* order_changed */
5878 };
5881 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5882 {
5883 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5885 {
5886 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5887 _("Avoid"),
5888 _("Make connectors avoid selected objects"),
5889 "connector_avoid",
5890 secondarySize );
5891 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5892 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5893 }
5895 {
5896 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5897 _("Ignore"),
5898 _("Make connectors ignore selected objects"),
5899 "connector_ignore",
5900 secondarySize );
5901 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5902 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5903 }
5905 EgeAdjustmentAction* eact = 0;
5907 // Spacing spinbox
5908 eact = create_adjustment_action( "ConnectorSpacingAction",
5909 _("Connector Spacing"), _("Spacing:"),
5910 _("The amount of space left around objects by auto-routing connectors"),
5911 "tools.connector", "spacing", defaultConnSpacing,
5912 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5913 0, 100, 1.0, 10.0,
5914 0, 0, 0,
5915 connector_spacing_changed, 1, 0 );
5916 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5918 // Graph (connector network) layout
5919 {
5920 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5921 _("Graph"),
5922 _("Nicely arrange selected connector network"),
5923 "graph_layout",
5924 secondarySize );
5925 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5926 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5927 }
5929 // Default connector length spinbox
5930 eact = create_adjustment_action( "ConnectorLengthAction",
5931 _("Connector Length"), _("Length:"),
5932 _("Ideal length for connectors when layout is applied"),
5933 "tools.connector", "length", 100,
5934 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5935 10, 1000, 10.0, 100.0,
5936 0, 0, 0,
5937 connector_length_changed, 1, 0 );
5938 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5941 // Directed edges toggle button
5942 {
5943 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5944 _("Downwards"),
5945 _("Make connectors with end-markers (arrows) point downwards"),
5946 "directed_graph",
5947 Inkscape::ICON_SIZE_DECORATION );
5948 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5950 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5951 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5952 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5954 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5955 }
5957 // Avoid overlaps toggle button
5958 {
5959 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5960 _("Remove overlaps"),
5961 _("Do not allow overlapping shapes"),
5962 "remove_overlaps",
5963 Inkscape::ICON_SIZE_DECORATION );
5964 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5966 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5967 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5968 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5970 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5971 }
5973 // Code to watch for changes to the connector-spacing attribute in
5974 // the XML.
5975 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5976 g_assert(repr != NULL);
5978 purge_repr_listener( holder, holder );
5980 if (repr) {
5981 g_object_set_data( holder, "repr", repr );
5982 Inkscape::GC::anchor(repr);
5983 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5984 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5985 }
5986 } // end of sp_connector_toolbox_prep()
5989 //#########################
5990 //## Paintbucket ##
5991 //#########################
5993 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5994 {
5995 gint channels = ege_select_one_action_get_active( act );
5996 flood_channels_set_channels( channels );
5997 }
5999 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6000 {
6001 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6002 }
6004 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6005 {
6006 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6007 }
6009 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6010 {
6011 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6012 SPUnit const *unit = tracker->getActiveUnit();
6014 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6016 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6017 }
6019 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
6020 {
6021 // FIXME: make defaults settable via Inkscape Options
6022 struct KeyValue {
6023 char const *key;
6024 double value;
6025 } const key_values[] = {
6026 {"threshold", 15},
6027 {"offset", 0.0}
6028 };
6030 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6031 KeyValue const &kv = key_values[i];
6032 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
6033 if ( adj ) {
6034 gtk_adjustment_set_value(adj, kv.value);
6035 }
6036 }
6038 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
6039 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6040 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
6041 ege_select_one_action_set_active( autogap_action, 0 );
6042 }
6044 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6045 {
6046 EgeAdjustmentAction* eact = 0;
6048 {
6049 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6051 GList* items = 0;
6052 gint count = 0;
6053 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6054 {
6055 GtkTreeIter iter;
6056 gtk_list_store_append( model, &iter );
6057 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6058 count++;
6059 }
6060 g_list_free( items );
6061 items = 0;
6062 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6063 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6064 ege_select_one_action_set_appearance( act1, "compact" );
6065 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6066 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6067 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6068 g_object_set_data( holder, "channels_action", act1 );
6069 }
6071 // Spacing spinbox
6072 {
6073 eact = create_adjustment_action(
6074 "ThresholdAction",
6075 _("Fill Threshold"), _("Threshold:"),
6076 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6077 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6078 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6079 0, 0, 0,
6080 paintbucket_threshold_changed, 1, 0 );
6082 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6083 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6084 }
6086 // Create the units menu.
6087 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6088 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6089 if (stored_unit)
6090 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6091 g_object_set_data( holder, "tracker", tracker );
6092 {
6093 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6094 gtk_action_group_add_action( mainActions, act );
6095 }
6097 // Offset spinbox
6098 {
6099 eact = create_adjustment_action(
6100 "OffsetAction",
6101 _("Grow/shrink by"), _("Grow/shrink by:"),
6102 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6103 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6104 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6105 0, 0, 0,
6106 paintbucket_offset_changed, 1, 2);
6107 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6109 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6110 }
6112 /* Auto Gap */
6113 {
6114 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6116 GList* items = 0;
6117 gint count = 0;
6118 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6119 {
6120 GtkTreeIter iter;
6121 gtk_list_store_append( model, &iter );
6122 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6123 count++;
6124 }
6125 g_list_free( items );
6126 items = 0;
6127 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6128 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6129 ege_select_one_action_set_appearance( act2, "compact" );
6130 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6131 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6132 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6133 g_object_set_data( holder, "autogap_action", act2 );
6134 }
6136 /* Reset */
6137 {
6138 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6139 _("Defaults"),
6140 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6141 GTK_STOCK_CLEAR );
6142 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6143 gtk_action_group_add_action( mainActions, act );
6144 gtk_action_set_sensitive( act, TRUE );
6145 }
6147 }
6149 /*
6150 Local Variables:
6151 mode:c++
6152 c-file-style:"stroustrup"
6153 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6154 indent-tabs-mode:nil
6155 fill-column:99
6156 End:
6157 */
6158 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :