71c797bffd1452e40dfc57393d94cf36dfcc6b99
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 * Maximilian Albert <maximilian.albert@gmail.com>
18 *
19 * Copyright (C) 2004 David Turner
20 * Copyright (C) 2003 MenTaLguY
21 * Copyright (C) 1999-2008 authors
22 * Copyright (C) 2001-2002 Ximian, Inc.
23 *
24 * Released under GNU GPL, read the file 'COPYING' for more information
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <cstring>
32 #include <string>
34 #include <gtkmm.h>
35 #include <gtk/gtk.h>
36 #include <iostream>
37 #include <sstream>
39 #include "widgets/button.h"
40 #include "widgets/widget-sizes.h"
41 #include "widgets/spw-utilities.h"
42 #include "widgets/spinbutton-events.h"
43 #include "dialogs/text-edit.h"
44 #include "dialogs/dialog-events.h"
46 #include "ui/widget/style-swatch.h"
48 #include "prefs-utils.h"
49 #include "verbs.h"
50 #include "sp-namedview.h"
51 #include "desktop.h"
52 #include "desktop-handles.h"
53 #include "xml/repr.h"
54 #include "xml/node-event-vector.h"
55 #include "xml/attribute-record.h"
56 #include <glibmm/i18n.h>
57 #include "helper/unit-menu.h"
58 #include "helper/units.h"
59 #include "live_effects/effect.h"
61 #include "inkscape.h"
62 #include "conn-avoid-ref.h"
65 #include "select-toolbar.h"
66 #include "gradient-toolbar.h"
68 #include "connector-context.h"
69 #include "node-context.h"
70 #include "pen-context.h"
71 #include "lpe-tool-context.h"
72 #include "shape-editor.h"
73 #include "tweak-context.h"
74 #include "sp-rect.h"
75 #include "box3d.h"
76 #include "box3d-context.h"
77 #include "sp-star.h"
78 #include "sp-spiral.h"
79 #include "sp-ellipse.h"
80 #include "sp-text.h"
81 #include "sp-flowtext.h"
82 #include "sp-clippath.h"
83 #include "sp-mask.h"
84 #include "style.h"
85 #include "tools-switch.h"
86 #include "selection.h"
87 #include "selection-chemistry.h"
88 #include "document-private.h"
89 #include "desktop-style.h"
90 #include "../libnrtype/font-lister.h"
91 #include "../libnrtype/font-instance.h"
92 #include "../connection-pool.h"
93 #include "../prefs-utils.h"
94 #include "../inkscape-stock.h"
95 #include "icon.h"
96 #include "graphlayout/graphlayout.h"
97 #include "interface.h"
98 #include "shortcuts.h"
100 #include "mod360.h"
102 #include "toolbox.h"
104 #include "flood-context.h"
106 #include "ink-action.h"
107 #include "ege-adjustment-action.h"
108 #include "ege-output-action.h"
109 #include "ege-select-one-action.h"
110 #include "helper/unit-tracker.h"
111 #include "live_effects/lpe-angle_bisector.h"
113 #include "svg/css-ostringstream.h"
115 #include "widgets/calligraphic-profile-rename.h"
117 using Inkscape::UnitTracker;
119 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
120 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
122 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
135 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
143 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
144 static Inkscape::IconSize sizeChoices[] = {
145 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
146 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
147 Inkscape::ICON_SIZE_MENU
148 };
149 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
150 return sizeChoices[index];
151 }
153 static struct {
154 gchar const *type_name;
155 gchar const *data_name;
156 sp_verb_t verb;
157 sp_verb_t doubleclick_verb;
158 } const tools[] = {
159 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
160 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
161 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
162 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
163 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
164 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
165 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
166 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
167 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
168 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
169 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
170 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
171 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
172 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
173 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
174 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
175 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
176 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
177 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
178 { NULL, NULL, 0, 0 }
179 };
181 static struct {
182 gchar const *type_name;
183 gchar const *data_name;
184 GtkWidget *(*create_func)(SPDesktop *desktop);
185 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
186 gchar const *ui_name;
187 gint swatch_verb_id;
188 gchar const *swatch_tool;
189 gchar const *swatch_tip;
190 } const aux_toolboxes[] = {
191 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
192 SP_VERB_INVALID, 0, 0},
193 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
194 SP_VERB_INVALID, 0, 0},
195 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
196 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
197 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
198 SP_VERB_INVALID, 0, 0},
199 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
200 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
201 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
202 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
203 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
204 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
205 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
206 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
207 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
208 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
209 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
210 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
211 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
212 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
213 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
214 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
215 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
216 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
217 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
218 SP_VERB_CONTEXT_LPETOOL_PREFS, "tools.lpetool", _("TBD")},
219 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
220 SP_VERB_INVALID, 0, 0},
221 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
222 SP_VERB_INVALID, 0, 0},
223 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
224 SP_VERB_INVALID, 0, 0},
225 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
226 SP_VERB_INVALID, 0, 0},
227 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
228 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
229 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
230 };
232 #define TOOLBAR_SLIDER_HINT "full"
234 static gchar const * ui_descr =
235 "<ui>"
236 " <toolbar name='SelectToolbar'>"
237 " <toolitem action='EditSelectAll' />"
238 " <toolitem action='EditSelectAllInAllLayers' />"
239 " <toolitem action='EditDeselect' />"
240 " <separator />"
241 " <toolitem action='ObjectRotate90CCW' />"
242 " <toolitem action='ObjectRotate90' />"
243 " <toolitem action='ObjectFlipHorizontally' />"
244 " <toolitem action='ObjectFlipVertically' />"
245 " <separator />"
246 " <toolitem action='SelectionToBack' />"
247 " <toolitem action='SelectionLower' />"
248 " <toolitem action='SelectionRaise' />"
249 " <toolitem action='SelectionToFront' />"
250 " <separator />"
251 " <toolitem action='XAction' />"
252 " <toolitem action='YAction' />"
253 " <toolitem action='WidthAction' />"
254 " <toolitem action='LockAction' />"
255 " <toolitem action='HeightAction' />"
256 " <toolitem action='UnitsAction' />"
257 " <separator />"
258 " <toolitem action='transform_affect_label' />"
259 " <toolitem action='transform_stroke' />"
260 " <toolitem action='transform_corners' />"
261 " <toolitem action='transform_gradient' />"
262 " <toolitem action='transform_pattern' />"
263 " </toolbar>"
265 " <toolbar name='NodeToolbar'>"
266 " <toolitem action='NodeInsertAction' />"
267 " <toolitem action='NodeDeleteAction' />"
268 " <separator />"
269 " <toolitem action='NodeJoinAction' />"
270 " <toolitem action='NodeBreakAction' />"
271 " <separator />"
272 " <toolitem action='NodeJoinSegmentAction' />"
273 " <toolitem action='NodeDeleteSegmentAction' />"
274 " <separator />"
275 " <toolitem action='NodeCuspAction' />"
276 " <toolitem action='NodeSmoothAction' />"
277 " <toolitem action='NodeSymmetricAction' />"
278 " <separator />"
279 " <toolitem action='NodeLineAction' />"
280 " <toolitem action='NodeCurveAction' />"
281 " <separator />"
282 " <toolitem action='ObjectToPath' />"
283 " <toolitem action='StrokeToPath' />"
284 " <separator />"
285 " <toolitem action='NodeXAction' />"
286 " <toolitem action='NodeYAction' />"
287 " <toolitem action='NodeUnitsAction' />"
288 " <separator />"
289 " <toolitem action='ObjectEditClipPathAction' />"
290 " <toolitem action='ObjectEditMaskPathAction' />"
291 " <toolitem action='EditNextLPEParameterAction' />"
292 " <separator />"
293 " <toolitem action='NodesShowHandlesAction' />"
294 " <toolitem action='NodesShowHelperpath' />"
295 " </toolbar>"
297 " <toolbar name='TweakToolbar'>"
298 " <toolitem action='TweakWidthAction' />"
299 " <separator />"
300 " <toolitem action='TweakForceAction' />"
301 " <toolitem action='TweakPressureAction' />"
302 " <separator />"
303 " <toolitem action='TweakModeAction' />"
304 " <separator />"
305 " <toolitem action='TweakFidelityAction' />"
306 " <separator />"
307 " <toolitem action='TweakChannelsLabel' />"
308 " <toolitem action='TweakDoH' />"
309 " <toolitem action='TweakDoS' />"
310 " <toolitem action='TweakDoL' />"
311 " <toolitem action='TweakDoO' />"
312 " </toolbar>"
314 " <toolbar name='ZoomToolbar'>"
315 " <toolitem action='ZoomIn' />"
316 " <toolitem action='ZoomOut' />"
317 " <separator />"
318 " <toolitem action='Zoom1:0' />"
319 " <toolitem action='Zoom1:2' />"
320 " <toolitem action='Zoom2:1' />"
321 " <separator />"
322 " <toolitem action='ZoomSelection' />"
323 " <toolitem action='ZoomDrawing' />"
324 " <toolitem action='ZoomPage' />"
325 " <toolitem action='ZoomPageWidth' />"
326 " <separator />"
327 " <toolitem action='ZoomPrev' />"
328 " <toolitem action='ZoomNext' />"
329 " </toolbar>"
331 " <toolbar name='StarToolbar'>"
332 " <separator />"
333 " <toolitem action='StarStateAction' />"
334 " <separator />"
335 " <toolitem action='FlatAction' />"
336 " <separator />"
337 " <toolitem action='MagnitudeAction' />"
338 " <toolitem action='SpokeAction' />"
339 " <toolitem action='RoundednessAction' />"
340 " <toolitem action='RandomizationAction' />"
341 " <separator />"
342 " <toolitem action='StarResetAction' />"
343 " </toolbar>"
345 " <toolbar name='RectToolbar'>"
346 " <toolitem action='RectStateAction' />"
347 " <toolitem action='RectWidthAction' />"
348 " <toolitem action='RectHeightAction' />"
349 " <toolitem action='RadiusXAction' />"
350 " <toolitem action='RadiusYAction' />"
351 " <toolitem action='RectUnitsAction' />"
352 " <separator />"
353 " <toolitem action='RectResetAction' />"
354 " </toolbar>"
356 " <toolbar name='3DBoxToolbar'>"
357 " <toolitem action='3DBoxAngleXAction' />"
358 " <toolitem action='3DBoxVPXStateAction' />"
359 " <separator />"
360 " <toolitem action='3DBoxAngleYAction' />"
361 " <toolitem action='3DBoxVPYStateAction' />"
362 " <separator />"
363 " <toolitem action='3DBoxAngleZAction' />"
364 " <toolitem action='3DBoxVPZStateAction' />"
365 " </toolbar>"
367 " <toolbar name='SpiralToolbar'>"
368 " <toolitem action='SpiralStateAction' />"
369 " <toolitem action='SpiralRevolutionAction' />"
370 " <toolitem action='SpiralExpansionAction' />"
371 " <toolitem action='SpiralT0Action' />"
372 " <separator />"
373 " <toolitem action='SpiralResetAction' />"
374 " </toolbar>"
376 " <toolbar name='PenToolbar'>"
377 " <toolitem action='FreehandModeActionPen' />"
378 " <separator />"
379 " <toolitem action='SetPenShapeAction'/>"
380 " </toolbar>"
382 " <toolbar name='PencilToolbar'>"
383 " <toolitem action='FreehandModeActionPencil' />"
384 " <separator />"
385 " <toolitem action='PencilToleranceAction' />"
386 " <separator />"
387 " <toolitem action='PencilResetAction' />"
388 " <separator />"
389 " <toolitem action='SetPencilShapeAction'/>"
390 " </toolbar>"
392 " <toolbar name='CalligraphyToolbar'>"
393 " <separator />"
394 " <toolitem action='SetProfileAction'/>"
395 " <separator />"
396 " <toolitem action='CalligraphyWidthAction' />"
397 " <toolitem action='PressureAction' />"
398 " <toolitem action='TraceAction' />"
399 " <toolitem action='ThinningAction' />"
400 " <separator />"
401 " <toolitem action='AngleAction' />"
402 " <toolitem action='TiltAction' />"
403 " <toolitem action='FixationAction' />"
404 " <separator />"
405 " <toolitem action='CapRoundingAction' />"
406 " <separator />"
407 " <toolitem action='TremorAction' />"
408 " <toolitem action='WiggleAction' />"
409 " <toolitem action='MassAction' />"
410 " <separator />"
411 " </toolbar>"
413 " <toolbar name='ArcToolbar'>"
414 " <toolitem action='ArcStateAction' />"
415 " <separator />"
416 " <toolitem action='ArcStartAction' />"
417 " <toolitem action='ArcEndAction' />"
418 " <separator />"
419 " <toolitem action='ArcOpenAction' />"
420 " <separator />"
421 " <toolitem action='ArcResetAction' />"
422 " <separator />"
423 " </toolbar>"
425 " <toolbar name='PaintbucketToolbar'>"
426 " <toolitem action='ChannelsAction' />"
427 " <separator />"
428 " <toolitem action='ThresholdAction' />"
429 " <separator />"
430 " <toolitem action='OffsetAction' />"
431 " <toolitem action='PaintbucketUnitsAction' />"
432 " <separator />"
433 " <toolitem action='AutoGapAction' />"
434 " <separator />"
435 " <toolitem action='PaintbucketResetAction' />"
436 " </toolbar>"
438 " <toolbar name='EraserToolbar'>"
439 " <toolitem action='EraserWidthAction' />"
440 " <separator />"
441 " <toolitem action='EraserModeAction' />"
442 " </toolbar>"
444 " <toolbar name='LPEToolToolbar'>"
445 " <toolitem action='LPEToolModeAction' />"
446 " <separator />"
447 " <toolitem action='LPEShowBBoxAction' />"
448 " </toolbar>"
450 " <toolbar name='DropperToolbar'>"
451 " <toolitem action='DropperOpacityAction' />"
452 " <toolitem action='DropperPickAlphaAction' />"
453 " <toolitem action='DropperSetAlphaAction' />"
454 " </toolbar>"
456 " <toolbar name='ConnectorToolbar'>"
457 " <toolitem action='ConnectorAvoidAction' />"
458 " <toolitem action='ConnectorIgnoreAction' />"
459 " <toolitem action='ConnectorSpacingAction' />"
460 " <toolitem action='ConnectorGraphAction' />"
461 " <toolitem action='ConnectorLengthAction' />"
462 " <toolitem action='ConnectorDirectedAction' />"
463 " <toolitem action='ConnectorOverlapAction' />"
464 " </toolbar>"
466 "</ui>"
467 ;
469 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
471 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
473 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
474 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
476 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
477 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
479 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
480 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
484 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
485 Inkscape::UI::View::View *view, GtkTooltips *tt);
487 class VerbAction : public Gtk::Action {
488 public:
489 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
491 virtual ~VerbAction();
492 virtual void set_active(bool active = true);
494 protected:
495 virtual Gtk::Widget* create_menu_item_vfunc();
496 virtual Gtk::Widget* create_tool_item_vfunc();
498 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
499 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
501 virtual void on_activate();
503 private:
504 Inkscape::Verb* verb;
505 Inkscape::Verb* verb2;
506 Inkscape::UI::View::View *view;
507 GtkTooltips *tooltips;
508 bool active;
510 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
511 };
514 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
515 {
516 Glib::RefPtr<VerbAction> result;
517 SPAction *action = verb->get_action(view);
518 if ( action ) {
519 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
520 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
521 }
523 return result;
524 }
526 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
527 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
528 verb(verb),
529 verb2(verb2),
530 view(view),
531 tooltips(tooltips),
532 active(false)
533 {
534 }
536 VerbAction::~VerbAction()
537 {
538 }
540 Gtk::Widget* VerbAction::create_menu_item_vfunc()
541 {
542 // First call in to get the icon rendered if present in SVG
543 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
544 delete widget;
545 widget = 0;
547 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
548 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
549 return widg;
550 }
552 Gtk::Widget* VerbAction::create_tool_item_vfunc()
553 {
554 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
555 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
556 GtkWidget* toolbox = 0;
557 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
558 SP_BUTTON_TYPE_TOGGLE,
559 verb,
560 verb2,
561 view,
562 tooltips );
563 if ( active ) {
564 sp_button_toggle_set_down( SP_BUTTON(button), active);
565 }
566 gtk_widget_show_all( button );
567 Gtk::Widget* wrapped = Glib::wrap(button);
568 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
569 holder->add(*wrapped);
571 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
572 return holder;
573 }
575 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
576 {
577 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
578 Gtk::Action::connect_proxy_vfunc(proxy);
579 }
581 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
582 {
583 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
584 Gtk::Action::disconnect_proxy_vfunc(proxy);
585 }
587 void VerbAction::set_active(bool active)
588 {
589 this->active = active;
590 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
591 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
592 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
593 if (ti) {
594 // *should* have one child that is the SPButton
595 Gtk::Widget* child = ti->get_child();
596 if ( child && SP_IS_BUTTON(child->gobj()) ) {
597 SPButton* button = SP_BUTTON(child->gobj());
598 sp_button_toggle_set_down( button, active );
599 }
600 }
601 }
602 }
604 void VerbAction::on_activate()
605 {
606 if ( verb ) {
607 SPAction *action = verb->get_action(view);
608 if ( action ) {
609 sp_action_perform(action, 0);
610 }
611 }
612 }
614 /* Global text entry widgets necessary for update */
615 /* GtkWidget *dropper_rgb_entry,
616 *dropper_opacity_entry ; */
617 // should be made a private member once this is converted to class
619 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
620 connection->disconnect();
621 delete connection;
622 }
624 static void purge_repr_listener( GObject* obj, GObject* tbl )
625 {
626 (void)obj;
627 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
628 if (oldrepr) { // remove old listener
629 sp_repr_remove_listener_by_data(oldrepr, tbl);
630 Inkscape::GC::release(oldrepr);
631 oldrepr = 0;
632 g_object_set_data( tbl, "repr", NULL );
633 }
634 }
636 GtkWidget *
637 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
638 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
639 Inkscape::UI::View::View *view, GtkTooltips *tt)
640 {
641 SPAction *action = verb->get_action(view);
642 if (!action) return NULL;
644 SPAction *doubleclick_action;
645 if (doubleclick_verb)
646 doubleclick_action = doubleclick_verb->get_action(view);
647 else
648 doubleclick_action = NULL;
650 /* fixme: Handle sensitive/unsensitive */
651 /* fixme: Implement sp_button_new_from_action */
652 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
653 gtk_widget_show(b);
656 unsigned int shortcut = sp_shortcut_get_primary(verb);
657 if (shortcut) {
658 gchar key[256];
659 sp_ui_shortcut_string(shortcut, key);
660 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
661 if ( t ) {
662 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
663 }
664 g_free(tip);
665 } else {
666 if ( t ) {
667 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
668 }
669 }
671 return b;
672 }
675 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
676 {
677 SPAction* targetAction = SP_ACTION(user_data);
678 if ( targetAction ) {
679 sp_action_perform( targetAction, NULL );
680 }
681 }
683 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
684 {
685 if ( data ) {
686 GtkAction* act = GTK_ACTION(data);
687 gtk_action_set_sensitive( act, sensitive );
688 }
689 }
691 static SPActionEventVector action_event_vector = {
692 {NULL},
693 NULL,
694 NULL,
695 sp_action_action_set_sensitive,
696 NULL,
697 NULL
698 };
700 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
701 {
702 GtkAction* act = 0;
704 SPAction* targetAction = verb->get_action(view);
705 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
706 act = GTK_ACTION(inky);
707 gtk_action_set_sensitive( act, targetAction->sensitive );
709 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
711 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
712 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
714 return act;
715 }
717 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
718 {
719 Inkscape::UI::View::View *view = desktop;
720 gint verbsToUse[] = {
721 // disabled until we have icons for them:
722 //find
723 //SP_VERB_EDIT_TILE,
724 //SP_VERB_EDIT_UNTILE,
725 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
726 SP_VERB_DIALOG_DISPLAY,
727 SP_VERB_DIALOG_FILL_STROKE,
728 SP_VERB_DIALOG_NAMEDVIEW,
729 SP_VERB_DIALOG_TEXT,
730 SP_VERB_DIALOG_XML_EDITOR,
731 SP_VERB_EDIT_CLONE,
732 SP_VERB_EDIT_COPY,
733 SP_VERB_EDIT_CUT,
734 SP_VERB_EDIT_DUPLICATE,
735 SP_VERB_EDIT_PASTE,
736 SP_VERB_EDIT_REDO,
737 SP_VERB_EDIT_UNDO,
738 SP_VERB_EDIT_UNLINK_CLONE,
739 SP_VERB_FILE_EXPORT,
740 SP_VERB_FILE_IMPORT,
741 SP_VERB_FILE_NEW,
742 SP_VERB_FILE_OPEN,
743 SP_VERB_FILE_PRINT,
744 SP_VERB_FILE_SAVE,
745 SP_VERB_OBJECT_TO_CURVE,
746 SP_VERB_SELECTION_GROUP,
747 SP_VERB_SELECTION_OUTLINE,
748 SP_VERB_SELECTION_UNGROUP,
749 SP_VERB_ZOOM_1_1,
750 SP_VERB_ZOOM_1_2,
751 SP_VERB_ZOOM_2_1,
752 SP_VERB_ZOOM_DRAWING,
753 SP_VERB_ZOOM_IN,
754 SP_VERB_ZOOM_NEXT,
755 SP_VERB_ZOOM_OUT,
756 SP_VERB_ZOOM_PAGE,
757 SP_VERB_ZOOM_PAGE_WIDTH,
758 SP_VERB_ZOOM_PREV,
759 SP_VERB_ZOOM_SELECTION,
760 };
762 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
764 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
765 Glib::RefPtr<Gtk::ActionGroup> mainActions;
766 if ( groups.find(desktop) != groups.end() ) {
767 mainActions = groups[desktop];
768 }
770 if ( !mainActions ) {
771 mainActions = Gtk::ActionGroup::create("main");
772 groups[desktop] = mainActions;
773 }
775 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
776 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
777 if ( verb ) {
778 if (!mainActions->get_action(verb->get_id())) {
779 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
780 mainActions->add(Glib::wrap(act));
781 }
782 }
783 }
785 if ( !mainActions->get_action("ToolZoom") ) {
786 GtkTooltips *tt = gtk_tooltips_new();
787 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
788 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
789 if ( va ) {
790 mainActions->add(va);
791 if ( i == 0 ) {
792 va->set_active(true);
793 }
794 }
795 }
796 }
799 return mainActions;
800 }
803 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
804 {
805 gtk_widget_set_size_request( widget,
806 widget->allocation.width,
807 widget->allocation.height );
808 }
810 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
811 {
812 gtk_widget_set_size_request( widget, -1, -1 );
813 }
817 GtkWidget *
818 sp_tool_toolbox_new()
819 {
820 GtkTooltips *tt = gtk_tooltips_new();
821 GtkWidget* tb = gtk_toolbar_new();
822 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
823 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
825 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
826 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
828 gtk_widget_set_sensitive(tb, FALSE);
830 GtkWidget *hb = gtk_handle_box_new();
831 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
832 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
833 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
835 gtk_container_add(GTK_CONTAINER(hb), tb);
836 gtk_widget_show(GTK_WIDGET(tb));
838 sigc::connection* conn = new sigc::connection;
839 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
841 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
842 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
844 return hb;
845 }
847 GtkWidget *
848 sp_aux_toolbox_new()
849 {
850 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
852 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
854 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
856 gtk_widget_set_sensitive(tb, FALSE);
858 GtkWidget *hb = gtk_handle_box_new();
859 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
860 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
861 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
863 gtk_container_add(GTK_CONTAINER(hb), tb);
864 gtk_widget_show(GTK_WIDGET(tb));
866 sigc::connection* conn = new sigc::connection;
867 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
869 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
870 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
872 return hb;
873 }
875 //####################################
876 //# Commands Bar
877 //####################################
879 GtkWidget *
880 sp_commands_toolbox_new()
881 {
882 GtkWidget *tb = gtk_toolbar_new();
884 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
885 gtk_widget_set_sensitive(tb, FALSE);
887 GtkWidget *hb = gtk_handle_box_new();
888 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
889 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
890 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
892 gtk_container_add(GTK_CONTAINER(hb), tb);
893 gtk_widget_show(GTK_WIDGET(tb));
895 sigc::connection* conn = new sigc::connection;
896 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
898 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
899 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
901 return hb;
902 }
905 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
906 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
907 gchar const *path, gchar const *data, gdouble def,
908 GtkWidget *focusTarget,
909 GtkWidget *us,
910 GObject *dataKludge,
911 gboolean altx, gchar const *altx_mark,
912 gdouble lower, gdouble upper, gdouble step, gdouble page,
913 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
914 void (*callback)(GtkAdjustment *, GObject *),
915 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
916 {
917 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
918 lower, upper, step, page, page ) );
919 if (us) {
920 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
921 }
923 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
925 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
926 if ( shortLabel ) {
927 g_object_set( act, "short_label", shortLabel, NULL );
928 }
930 if ( (descrCount > 0) && descrLabels && descrValues ) {
931 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
932 }
934 if ( focusTarget ) {
935 ege_adjustment_action_set_focuswidget( act, focusTarget );
936 }
938 if ( altx && altx_mark ) {
939 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
940 }
942 if ( dataKludge ) {
943 g_object_set_data( dataKludge, data, adj );
944 }
946 // Using a cast just to make sure we pass in the right kind of function pointer
947 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
949 return act;
950 }
953 //####################################
954 //# node editing callbacks
955 //####################################
957 /**
958 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
959 */
960 static ShapeEditor *get_current_shape_editor()
961 {
962 if (!SP_ACTIVE_DESKTOP) {
963 return NULL;
964 }
966 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
968 if (!SP_IS_NODE_CONTEXT(event_context)) {
969 return NULL;
970 }
972 return SP_NODE_CONTEXT(event_context)->shape_editor;
973 }
976 void
977 sp_node_path_edit_add(void)
978 {
979 ShapeEditor *shape_editor = get_current_shape_editor();
980 if (shape_editor) shape_editor->add_node();
981 }
983 void
984 sp_node_path_edit_delete(void)
985 {
986 ShapeEditor *shape_editor = get_current_shape_editor();
987 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
988 }
990 void
991 sp_node_path_edit_delete_segment(void)
992 {
993 ShapeEditor *shape_editor = get_current_shape_editor();
994 if (shape_editor) shape_editor->delete_segment();
995 }
997 void
998 sp_node_path_edit_break(void)
999 {
1000 ShapeEditor *shape_editor = get_current_shape_editor();
1001 if (shape_editor) shape_editor->break_at_nodes();
1002 }
1004 void
1005 sp_node_path_edit_join(void)
1006 {
1007 ShapeEditor *shape_editor = get_current_shape_editor();
1008 if (shape_editor) shape_editor->join_nodes();
1009 }
1011 void
1012 sp_node_path_edit_join_segment(void)
1013 {
1014 ShapeEditor *shape_editor = get_current_shape_editor();
1015 if (shape_editor) shape_editor->join_segments();
1016 }
1018 void
1019 sp_node_path_edit_toline(void)
1020 {
1021 ShapeEditor *shape_editor = get_current_shape_editor();
1022 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1023 }
1025 void
1026 sp_node_path_edit_tocurve(void)
1027 {
1028 ShapeEditor *shape_editor = get_current_shape_editor();
1029 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1030 }
1032 void
1033 sp_node_path_edit_cusp(void)
1034 {
1035 ShapeEditor *shape_editor = get_current_shape_editor();
1036 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1037 }
1039 void
1040 sp_node_path_edit_smooth(void)
1041 {
1042 ShapeEditor *shape_editor = get_current_shape_editor();
1043 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1044 }
1046 void
1047 sp_node_path_edit_symmetrical(void)
1048 {
1049 ShapeEditor *shape_editor = get_current_shape_editor();
1050 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1051 }
1053 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1054 bool show = gtk_toggle_action_get_active( act );
1055 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1056 ShapeEditor *shape_editor = get_current_shape_editor();
1057 if (shape_editor) shape_editor->show_handles(show);
1058 }
1060 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1061 bool show = gtk_toggle_action_get_active( act );
1062 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1063 ShapeEditor *shape_editor = get_current_shape_editor();
1064 if (shape_editor) shape_editor->show_helperpath(show);
1065 }
1067 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1068 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1069 }
1071 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1072 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1073 }
1075 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1076 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1077 }
1079 /* is called when the node selection is modified */
1080 static void
1081 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1082 {
1083 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1084 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1085 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1086 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1088 // quit if run by the attr_changed listener
1089 if (g_object_get_data( tbl, "freeze" )) {
1090 return;
1091 }
1093 // in turn, prevent listener from responding
1094 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1096 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1097 SPUnit const *unit = tracker->getActiveUnit();
1099 ShapeEditor *shape_editor = get_current_shape_editor();
1100 if (shape_editor && shape_editor->has_nodepath()) {
1101 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1102 int n_selected = 0;
1103 if (nodepath) {
1104 n_selected = nodepath->numSelected();
1105 }
1107 if (n_selected == 0) {
1108 gtk_action_set_sensitive(xact, FALSE);
1109 gtk_action_set_sensitive(yact, FALSE);
1110 } else {
1111 gtk_action_set_sensitive(xact, TRUE);
1112 gtk_action_set_sensitive(yact, TRUE);
1113 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1114 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1116 if (n_selected == 1) {
1117 Geom::Point sel_node = nodepath->singleSelectedCoords();
1118 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1119 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1120 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1121 }
1122 } else {
1123 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1124 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1125 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1126 /* Note: Currently x and y will always have a value, even if the coordinates of the
1127 selected nodes don't coincide (in this case we use the coordinates of the center
1128 of the bounding box). So the entries are never set to zero. */
1129 // FIXME: Maybe we should clear the entry if several nodes are selected
1130 // instead of providing a kind of average value
1131 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1132 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1133 }
1134 }
1135 }
1136 } else {
1137 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1138 gtk_action_set_sensitive(xact, FALSE);
1139 gtk_action_set_sensitive(yact, FALSE);
1140 }
1142 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1143 }
1145 static void
1146 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1147 {
1148 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1150 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1151 SPUnit const *unit = tracker->getActiveUnit();
1153 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1154 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1155 }
1157 // quit if run by the attr_changed listener
1158 if (g_object_get_data( tbl, "freeze" )) {
1159 return;
1160 }
1162 // in turn, prevent listener from responding
1163 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1165 ShapeEditor *shape_editor = get_current_shape_editor();
1166 if (shape_editor && shape_editor->has_nodepath()) {
1167 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1168 if (!strcmp(value_name, "x")) {
1169 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1170 }
1171 if (!strcmp(value_name, "y")) {
1172 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1173 }
1174 }
1176 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1177 }
1179 static void
1180 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1181 {
1182 sp_node_path_value_changed(adj, tbl, "x");
1183 }
1185 static void
1186 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1187 {
1188 sp_node_path_value_changed(adj, tbl, "y");
1189 }
1191 void
1192 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1193 {
1194 {
1195 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1196 SPItem *item = selection->singleItem();
1197 if (item && SP_IS_LPE_ITEM(item)) {
1198 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1199 gtk_action_set_sensitive(w, TRUE);
1200 } else {
1201 gtk_action_set_sensitive(w, FALSE);
1202 }
1203 } else {
1204 gtk_action_set_sensitive(w, FALSE);
1205 }
1206 }
1208 {
1209 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1210 SPItem *item = selection->singleItem();
1211 if (item && item->clip_ref && item->clip_ref->getObject()) {
1212 gtk_action_set_sensitive(w, TRUE);
1213 } else {
1214 gtk_action_set_sensitive(w, FALSE);
1215 }
1216 }
1218 {
1219 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1220 SPItem *item = selection->singleItem();
1221 if (item && item->mask_ref && item->mask_ref->getObject()) {
1222 gtk_action_set_sensitive(w, TRUE);
1223 } else {
1224 gtk_action_set_sensitive(w, FALSE);
1225 }
1226 }
1227 }
1229 void
1230 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1231 {
1232 sp_node_toolbox_sel_changed (selection, tbl);
1233 }
1237 //################################
1238 //## Node Editing Toolbox ##
1239 //################################
1241 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1242 {
1243 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1244 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1245 g_object_set_data( holder, "tracker", tracker );
1247 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1249 {
1250 InkAction* inky = ink_action_new( "NodeInsertAction",
1251 _("Insert node"),
1252 _("Insert new nodes into selected segments"),
1253 "node_insert",
1254 secondarySize );
1255 g_object_set( inky, "short_label", _("Insert"), NULL );
1256 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1257 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1258 }
1260 {
1261 InkAction* inky = ink_action_new( "NodeDeleteAction",
1262 _("Delete node"),
1263 _("Delete selected nodes"),
1264 "node_delete",
1265 secondarySize );
1266 g_object_set( inky, "short_label", _("Delete"), NULL );
1267 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1268 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1269 }
1271 {
1272 InkAction* inky = ink_action_new( "NodeJoinAction",
1273 _("Join endnodes"),
1274 _("Join selected endnodes"),
1275 "node_join",
1276 secondarySize );
1277 g_object_set( inky, "short_label", _("Join"), NULL );
1278 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1279 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1280 }
1282 {
1283 InkAction* inky = ink_action_new( "NodeBreakAction",
1284 _("Break nodes"),
1285 _("Break path at selected nodes"),
1286 "node_break",
1287 secondarySize );
1288 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1289 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1290 }
1293 {
1294 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1295 _("Join with segment"),
1296 _("Join selected endnodes with a new segment"),
1297 "node_join_segment",
1298 secondarySize );
1299 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1300 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1301 }
1303 {
1304 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1305 _("Delete segment"),
1306 _("Delete segment between two non-endpoint nodes"),
1307 "node_delete_segment",
1308 secondarySize );
1309 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1310 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1311 }
1313 {
1314 InkAction* inky = ink_action_new( "NodeCuspAction",
1315 _("Node Cusp"),
1316 _("Make selected nodes corner"),
1317 "node_cusp",
1318 secondarySize );
1319 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1320 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1321 }
1323 {
1324 InkAction* inky = ink_action_new( "NodeSmoothAction",
1325 _("Node Smooth"),
1326 _("Make selected nodes smooth"),
1327 "node_smooth",
1328 secondarySize );
1329 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1330 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1331 }
1333 {
1334 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1335 _("Node Symmetric"),
1336 _("Make selected nodes symmetric"),
1337 "node_symmetric",
1338 secondarySize );
1339 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1340 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1341 }
1343 {
1344 InkAction* inky = ink_action_new( "NodeLineAction",
1345 _("Node Line"),
1346 _("Make selected segments lines"),
1347 "node_line",
1348 secondarySize );
1349 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1350 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1351 }
1353 {
1354 InkAction* inky = ink_action_new( "NodeCurveAction",
1355 _("Node Curve"),
1356 _("Make selected segments curves"),
1357 "node_curve",
1358 secondarySize );
1359 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1360 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1361 }
1363 {
1364 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1365 _("Show Handles"),
1366 _("Show the Bezier handles of selected nodes"),
1367 "nodes_show_handles",
1368 Inkscape::ICON_SIZE_DECORATION );
1369 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1370 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1371 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1372 }
1374 {
1375 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1376 _("Show Outline"),
1377 _("Show the outline of the path"),
1378 "nodes_show_helperpath",
1379 Inkscape::ICON_SIZE_DECORATION );
1380 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1381 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1382 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1383 }
1385 {
1386 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1387 _("Next path effect parameter"),
1388 _("Show next path effect parameter for editing"),
1389 "edit_next_parameter",
1390 Inkscape::ICON_SIZE_DECORATION );
1391 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1392 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1393 g_object_set_data( holder, "nodes_lpeedit", inky);
1394 }
1396 {
1397 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1398 _("Edit clipping path"),
1399 _("Edit the clipping path of the object"),
1400 "nodeedit-clippath",
1401 Inkscape::ICON_SIZE_DECORATION );
1402 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1403 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404 g_object_set_data( holder, "nodes_clippathedit", inky);
1405 }
1407 {
1408 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1409 _("Edit mask path"),
1410 _("Edit the mask of the object"),
1411 "nodeedit-mask",
1412 Inkscape::ICON_SIZE_DECORATION );
1413 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1414 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1415 g_object_set_data( holder, "nodes_maskedit", inky);
1416 }
1418 /* X coord of selected node(s) */
1419 {
1420 EgeAdjustmentAction* eact = 0;
1421 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1422 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1423 eact = create_adjustment_action( "NodeXAction",
1424 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1425 "tools.nodes", "Xcoord", 0,
1426 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1427 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1428 labels, values, G_N_ELEMENTS(labels),
1429 sp_node_path_x_value_changed );
1430 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1431 g_object_set_data( holder, "nodes_x_action", eact );
1432 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1433 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1434 }
1436 /* Y coord of selected node(s) */
1437 {
1438 EgeAdjustmentAction* eact = 0;
1439 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1440 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1441 eact = create_adjustment_action( "NodeYAction",
1442 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1443 "tools.nodes", "Ycoord", 0,
1444 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1445 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1446 labels, values, G_N_ELEMENTS(labels),
1447 sp_node_path_y_value_changed );
1448 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1449 g_object_set_data( holder, "nodes_y_action", eact );
1450 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1451 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1452 }
1454 // add the units menu
1455 {
1456 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1457 gtk_action_group_add_action( mainActions, act );
1458 }
1461 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1463 //watch selection
1464 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1466 sigc::connection *c_selection_changed =
1467 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1468 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1469 pool->add_connection ("selection-changed", c_selection_changed);
1471 sigc::connection *c_selection_modified =
1472 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1473 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1474 pool->add_connection ("selection-modified", c_selection_modified);
1476 sigc::connection *c_subselection_changed =
1477 new sigc::connection (desktop->connectToolSubselectionChanged
1478 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1479 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1481 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1483 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1484 } // end of sp_node_toolbox_prep()
1487 //########################
1488 //## Zoom Toolbox ##
1489 //########################
1491 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1492 {
1493 // no custom GtkAction setup needed
1494 } // end of sp_zoom_toolbox_prep()
1496 void
1497 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1498 {
1499 toolbox_set_desktop(toolbox,
1500 desktop,
1501 setup_tool_toolbox,
1502 update_tool_toolbox,
1503 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1504 "event_context_connection")));
1505 }
1508 void
1509 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1510 {
1511 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1512 desktop,
1513 setup_aux_toolbox,
1514 update_aux_toolbox,
1515 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1516 "event_context_connection")));
1517 }
1519 void
1520 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1521 {
1522 toolbox_set_desktop(toolbox,
1523 desktop,
1524 setup_commands_toolbox,
1525 update_commands_toolbox,
1526 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1527 "event_context_connection")));
1528 }
1530 static void
1531 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1532 {
1533 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1534 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1536 if (old_desktop) {
1537 GList *children, *iter;
1539 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1540 for ( iter = children ; iter ; iter = iter->next ) {
1541 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1542 }
1543 g_list_free(children);
1544 }
1546 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1548 if (desktop) {
1549 gtk_widget_set_sensitive(toolbox, TRUE);
1550 setup_func(toolbox, desktop);
1551 update_func(desktop, desktop->event_context, toolbox);
1552 *conn = desktop->connectEventContextChanged
1553 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1554 } else {
1555 gtk_widget_set_sensitive(toolbox, FALSE);
1556 }
1558 } // end of toolbox_set_desktop()
1561 static void
1562 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1563 {
1564 gchar const * descr =
1565 "<ui>"
1566 " <toolbar name='ToolToolbar'>"
1567 " <toolitem action='ToolSelector' />"
1568 " <toolitem action='ToolNode' />"
1569 " <toolitem action='ToolTweak' />"
1570 " <toolitem action='ToolZoom' />"
1571 " <toolitem action='ToolRect' />"
1572 " <toolitem action='Tool3DBox' />"
1573 " <toolitem action='ToolArc' />"
1574 " <toolitem action='ToolStar' />"
1575 " <toolitem action='ToolSpiral' />"
1576 " <toolitem action='ToolPencil' />"
1577 " <toolitem action='ToolPen' />"
1578 " <toolitem action='ToolCalligraphic' />"
1579 " <toolitem action='ToolEraser' />"
1580 " <toolitem action='ToolLPETool' />"
1581 " <toolitem action='ToolPaintBucket' />"
1582 " <toolitem action='ToolText' />"
1583 " <toolitem action='ToolConnector' />"
1584 " <toolitem action='ToolGradient' />"
1585 " <toolitem action='ToolDropper' />"
1586 " </toolbar>"
1587 "</ui>";
1588 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1589 GtkUIManager* mgr = gtk_ui_manager_new();
1590 GError* errVal = 0;
1592 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1593 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1595 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1596 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1597 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1598 }
1599 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1600 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1602 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1603 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1605 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1607 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1608 if ( child ) {
1609 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1610 }
1612 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1613 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1614 }
1617 static void
1618 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1619 {
1620 gchar const *const tname = ( eventcontext
1621 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1622 : NULL );
1623 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1625 for (int i = 0 ; tools[i].type_name ; i++ ) {
1626 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1627 if ( act ) {
1628 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1629 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1630 if ( verbAct ) {
1631 verbAct->set_active(setActive);
1632 }
1633 }
1634 }
1635 }
1637 static void
1638 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1639 {
1640 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1641 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1642 GtkUIManager* mgr = gtk_ui_manager_new();
1643 GError* errVal = 0;
1644 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1645 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1647 std::map<std::string, GtkWidget*> dataHolders;
1649 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1650 if ( aux_toolboxes[i].prep_func ) {
1651 // converted to GtkActions and UIManager
1653 GtkWidget* kludge = gtk_toolbar_new();
1654 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1655 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1656 dataHolders[aux_toolboxes[i].type_name] = kludge;
1657 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1658 } else {
1660 GtkWidget *sub_toolbox = 0;
1661 if (aux_toolboxes[i].create_func == NULL)
1662 sub_toolbox = sp_empty_toolbox_new(desktop);
1663 else {
1664 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1665 }
1667 gtk_size_group_add_widget( grouper, sub_toolbox );
1669 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1670 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1672 }
1673 }
1675 // Second pass to create toolbars *after* all GtkActions are created
1676 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1677 if ( aux_toolboxes[i].prep_func ) {
1678 // converted to GtkActions and UIManager
1680 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1682 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1683 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1685 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1686 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1687 g_free( tmp );
1688 tmp = 0;
1690 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1691 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1692 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1693 }
1694 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1697 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1699 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1700 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1701 swatch->setDesktop( desktop );
1702 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1703 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1704 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1705 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 );
1706 }
1708 gtk_widget_show_all( holder );
1709 sp_set_font_size_smaller( holder );
1711 gtk_size_group_add_widget( grouper, holder );
1713 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1714 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1715 }
1716 }
1718 g_object_unref( G_OBJECT(grouper) );
1719 }
1721 static void
1722 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1723 {
1724 gchar const *tname = ( eventcontext
1725 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1726 : NULL );
1727 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1728 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1729 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1730 gtk_widget_show_all(sub_toolbox);
1731 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1732 } else {
1733 gtk_widget_hide(sub_toolbox);
1734 }
1735 }
1736 }
1738 static void
1739 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1740 {
1741 gchar const * descr =
1742 "<ui>"
1743 " <toolbar name='CommandsToolbar'>"
1744 " <toolitem action='FileNew' />"
1745 " <toolitem action='FileOpen' />"
1746 " <toolitem action='FileSave' />"
1747 " <toolitem action='FilePrint' />"
1748 " <separator />"
1749 " <toolitem action='FileImport' />"
1750 " <toolitem action='FileExport' />"
1751 " <separator />"
1752 " <toolitem action='EditUndo' />"
1753 " <toolitem action='EditRedo' />"
1754 " <separator />"
1755 " <toolitem action='EditCopy' />"
1756 " <toolitem action='EditCut' />"
1757 " <toolitem action='EditPaste' />"
1758 " <separator />"
1759 " <toolitem action='ZoomSelection' />"
1760 " <toolitem action='ZoomDrawing' />"
1761 " <toolitem action='ZoomPage' />"
1762 " <separator />"
1763 " <toolitem action='EditDuplicate' />"
1764 " <toolitem action='EditClone' />"
1765 " <toolitem action='EditUnlinkClone' />"
1766 " <separator />"
1767 " <toolitem action='SelectionGroup' />"
1768 " <toolitem action='SelectionUnGroup' />"
1769 " <separator />"
1770 " <toolitem action='DialogFillStroke' />"
1771 " <toolitem action='DialogText' />"
1772 " <toolitem action='DialogXMLEditor' />"
1773 " <toolitem action='DialogAlignDistribute' />"
1774 " <separator />"
1775 " <toolitem action='DialogPreferences' />"
1776 " <toolitem action='DialogDocumentProperties' />"
1777 " </toolbar>"
1778 "</ui>";
1779 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1782 GtkUIManager* mgr = gtk_ui_manager_new();
1783 GError* errVal = 0;
1785 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1786 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1788 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1789 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1790 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1791 }
1793 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1794 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1796 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1797 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1800 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1802 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1803 if ( child ) {
1804 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1805 }
1807 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1808 }
1810 static void
1811 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1812 {
1813 }
1815 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1816 {
1817 gtk_widget_show(toolbox_toplevel);
1818 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1820 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1821 if (!shown_toolbox) {
1822 return;
1823 }
1824 gtk_widget_show(toolbox);
1826 gtk_widget_show_all(shown_toolbox);
1827 }
1829 static GtkWidget *
1830 sp_empty_toolbox_new(SPDesktop *desktop)
1831 {
1832 GtkWidget *tbl = gtk_toolbar_new();
1833 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1834 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1836 gtk_widget_show_all(tbl);
1837 sp_set_font_size_smaller (tbl);
1839 return tbl;
1840 }
1842 #define MODE_LABEL_WIDTH 70
1844 //########################
1845 //## Star ##
1846 //########################
1848 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1849 {
1850 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1852 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1853 // do not remember prefs if this call is initiated by an undo change, because undoing object
1854 // creation sets bogus values to its attributes before it is deleted
1855 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1856 }
1858 // quit if run by the attr_changed listener
1859 if (g_object_get_data( dataKludge, "freeze" )) {
1860 return;
1861 }
1863 // in turn, prevent listener from responding
1864 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1866 bool modmade = false;
1868 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1869 GSList const *items = selection->itemList();
1870 for (; items != NULL; items = items->next) {
1871 if (SP_IS_STAR((SPItem *) items->data)) {
1872 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1873 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1874 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1875 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1876 + M_PI / (gint)adj->value));
1877 SP_OBJECT((SPItem *) items->data)->updateRepr();
1878 modmade = true;
1879 }
1880 }
1881 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1882 _("Star: Change number of corners"));
1884 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1885 }
1887 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1888 {
1889 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1891 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1892 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1893 }
1895 // quit if run by the attr_changed listener
1896 if (g_object_get_data( dataKludge, "freeze" )) {
1897 return;
1898 }
1900 // in turn, prevent listener from responding
1901 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1903 bool modmade = false;
1904 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1905 GSList const *items = selection->itemList();
1906 for (; items != NULL; items = items->next) {
1907 if (SP_IS_STAR((SPItem *) items->data)) {
1908 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1910 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1911 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1912 if (r2 < r1) {
1913 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1914 } else {
1915 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1916 }
1918 SP_OBJECT((SPItem *) items->data)->updateRepr();
1919 modmade = true;
1920 }
1921 }
1923 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1924 _("Star: Change spoke ratio"));
1926 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1927 }
1929 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1930 {
1931 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1932 bool flat = ege_select_one_action_get_active( act ) == 0;
1934 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1935 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1936 flat ? "true" : "false" );
1937 }
1939 // quit if run by the attr_changed listener
1940 if (g_object_get_data( dataKludge, "freeze" )) {
1941 return;
1942 }
1944 // in turn, prevent listener from responding
1945 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1947 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1948 GSList const *items = selection->itemList();
1949 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1950 bool modmade = false;
1952 if ( prop_action ) {
1953 gtk_action_set_sensitive( prop_action, !flat );
1954 }
1956 for (; items != NULL; items = items->next) {
1957 if (SP_IS_STAR((SPItem *) items->data)) {
1958 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1959 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1960 SP_OBJECT((SPItem *) items->data)->updateRepr();
1961 modmade = true;
1962 }
1963 }
1965 if (modmade) {
1966 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1967 flat ? _("Make polygon") : _("Make star"));
1968 }
1970 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1971 }
1973 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1974 {
1975 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1977 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1978 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1979 }
1981 // quit if run by the attr_changed listener
1982 if (g_object_get_data( dataKludge, "freeze" )) {
1983 return;
1984 }
1986 // in turn, prevent listener from responding
1987 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1989 bool modmade = false;
1991 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1992 GSList const *items = selection->itemList();
1993 for (; items != NULL; items = items->next) {
1994 if (SP_IS_STAR((SPItem *) items->data)) {
1995 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1996 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1997 SP_OBJECT(items->data)->updateRepr();
1998 modmade = true;
1999 }
2000 }
2001 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2002 _("Star: Change rounding"));
2004 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2005 }
2007 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2008 {
2009 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2011 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2012 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2013 }
2015 // quit if run by the attr_changed listener
2016 if (g_object_get_data( dataKludge, "freeze" )) {
2017 return;
2018 }
2020 // in turn, prevent listener from responding
2021 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2023 bool modmade = false;
2025 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2026 GSList const *items = selection->itemList();
2027 for (; items != NULL; items = items->next) {
2028 if (SP_IS_STAR((SPItem *) items->data)) {
2029 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2030 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2031 SP_OBJECT(items->data)->updateRepr();
2032 modmade = true;
2033 }
2034 }
2035 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2036 _("Star: Change randomization"));
2038 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2039 }
2042 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2043 gchar const */*old_value*/, gchar const */*new_value*/,
2044 bool /*is_interactive*/, gpointer data)
2045 {
2046 GtkWidget *tbl = GTK_WIDGET(data);
2048 // quit if run by the _changed callbacks
2049 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2050 return;
2051 }
2053 // in turn, prevent callbacks from responding
2054 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2056 GtkAdjustment *adj = 0;
2058 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2059 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2061 if (!strcmp(name, "inkscape:randomized")) {
2062 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2063 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2064 } else if (!strcmp(name, "inkscape:rounded")) {
2065 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2066 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2067 } else if (!strcmp(name, "inkscape:flatsided")) {
2068 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2069 char const *flatsides = repr->attribute("inkscape:flatsided");
2070 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2071 if ( flatsides && !strcmp(flatsides,"false") ) {
2072 ege_select_one_action_set_active( flat_action, 1 );
2073 gtk_action_set_sensitive( prop_action, TRUE );
2074 } else {
2075 ege_select_one_action_set_active( flat_action, 0 );
2076 gtk_action_set_sensitive( prop_action, FALSE );
2077 }
2078 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2079 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2080 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2081 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2082 if (r2 < r1) {
2083 gtk_adjustment_set_value(adj, r2/r1);
2084 } else {
2085 gtk_adjustment_set_value(adj, r1/r2);
2086 }
2087 } else if (!strcmp(name, "sodipodi:sides")) {
2088 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2089 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2090 }
2092 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2093 }
2096 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2097 {
2098 NULL, /* child_added */
2099 NULL, /* child_removed */
2100 star_tb_event_attr_changed,
2101 NULL, /* content_changed */
2102 NULL /* order_changed */
2103 };
2106 /**
2107 * \param selection Should not be NULL.
2108 */
2109 static void
2110 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2111 {
2112 int n_selected = 0;
2113 Inkscape::XML::Node *repr = NULL;
2115 purge_repr_listener( tbl, tbl );
2117 for (GSList const *items = selection->itemList();
2118 items != NULL;
2119 items = items->next)
2120 {
2121 if (SP_IS_STAR((SPItem *) items->data)) {
2122 n_selected++;
2123 repr = SP_OBJECT_REPR((SPItem *) items->data);
2124 }
2125 }
2127 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2129 if (n_selected == 0) {
2130 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2131 } else if (n_selected == 1) {
2132 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2134 if (repr) {
2135 g_object_set_data( tbl, "repr", repr );
2136 Inkscape::GC::anchor(repr);
2137 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2138 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2139 }
2140 } else {
2141 // FIXME: implement averaging of all parameters for multiple selected stars
2142 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2143 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2144 }
2145 }
2148 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2149 {
2150 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2151 // callbacks to lump all the changes for all selected objects in one undo step
2153 GtkAdjustment *adj = 0;
2155 // fixme: make settable in prefs!
2156 gint mag = 5;
2157 gdouble prop = 0.5;
2158 gboolean flat = FALSE;
2159 gdouble randomized = 0;
2160 gdouble rounded = 0;
2162 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2163 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2165 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2166 gtk_action_set_sensitive( sb2, !flat );
2168 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2169 gtk_adjustment_set_value(adj, mag);
2170 gtk_adjustment_value_changed(adj);
2172 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2173 gtk_adjustment_set_value(adj, prop);
2174 gtk_adjustment_value_changed(adj);
2176 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2177 gtk_adjustment_set_value(adj, rounded);
2178 gtk_adjustment_value_changed(adj);
2180 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2181 gtk_adjustment_set_value(adj, randomized);
2182 gtk_adjustment_value_changed(adj);
2183 }
2186 void
2187 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2188 {
2189 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2190 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2191 GtkWidget *l = gtk_label_new(NULL);
2192 gtk_label_set_markup(GTK_LABEL(l), title);
2193 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2194 if ( GTK_IS_TOOLBAR(tbl) ) {
2195 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2196 } else {
2197 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2198 }
2199 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2200 }
2203 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2204 {
2205 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2207 {
2208 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2209 ege_output_action_set_use_markup( act, TRUE );
2210 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2211 g_object_set_data( holder, "mode_action", act );
2212 }
2214 {
2215 EgeAdjustmentAction* eact = 0;
2216 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2217 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2219 /* Flatsided checkbox */
2220 {
2221 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2223 GtkTreeIter iter;
2224 gtk_list_store_append( model, &iter );
2225 gtk_list_store_set( model, &iter,
2226 0, _("Polygon"),
2227 1, _("Regular polygon (with one handle) instead of a star"),
2228 2, "star_flat",
2229 -1 );
2231 gtk_list_store_append( model, &iter );
2232 gtk_list_store_set( model, &iter,
2233 0, _("Star"),
2234 1, _("Star instead of a regular polygon (with one handle)"),
2235 2, "star_angled",
2236 -1 );
2238 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2239 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2240 g_object_set_data( holder, "flat_action", act );
2242 ege_select_one_action_set_appearance( act, "full" );
2243 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2244 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2245 ege_select_one_action_set_icon_column( act, 2 );
2246 ege_select_one_action_set_icon_size( act, secondarySize );
2247 ege_select_one_action_set_tooltip_column( act, 1 );
2249 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2250 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2251 }
2253 /* Magnitude */
2254 {
2255 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2256 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2257 eact = create_adjustment_action( "MagnitudeAction",
2258 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2259 "tools.shapes.star", "magnitude", 3,
2260 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2261 3, 1024, 1, 5,
2262 labels, values, G_N_ELEMENTS(labels),
2263 sp_stb_magnitude_value_changed,
2264 1.0, 0 );
2265 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2266 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2267 }
2269 /* Spoke ratio */
2270 {
2271 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2272 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2273 eact = create_adjustment_action( "SpokeAction",
2274 _("Spoke ratio"), _("Spoke ratio:"),
2275 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2276 // Base radius is the same for the closest handle.
2277 _("Base radius to tip radius ratio"),
2278 "tools.shapes.star", "proportion", 0.5,
2279 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2280 0.01, 1.0, 0.01, 0.1,
2281 labels, values, G_N_ELEMENTS(labels),
2282 sp_stb_proportion_value_changed );
2283 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2284 g_object_set_data( holder, "prop_action", eact );
2285 }
2287 if ( !isFlatSided ) {
2288 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2289 } else {
2290 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2291 }
2293 /* Roundedness */
2294 {
2295 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2296 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2297 eact = create_adjustment_action( "RoundednessAction",
2298 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2299 "tools.shapes.star", "rounded", 0.0,
2300 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2301 -10.0, 10.0, 0.01, 0.1,
2302 labels, values, G_N_ELEMENTS(labels),
2303 sp_stb_rounded_value_changed );
2304 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2305 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2306 }
2308 /* Randomization */
2309 {
2310 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2311 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2312 eact = create_adjustment_action( "RandomizationAction",
2313 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2314 "tools.shapes.star", "randomized", 0.0,
2315 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2316 -10.0, 10.0, 0.001, 0.01,
2317 labels, values, G_N_ELEMENTS(labels),
2318 sp_stb_randomized_value_changed, 0.1, 3 );
2319 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2320 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2321 }
2322 }
2324 {
2325 /* Reset */
2326 {
2327 GtkAction* act = gtk_action_new( "StarResetAction",
2328 _("Defaults"),
2329 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2330 GTK_STOCK_CLEAR );
2331 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2332 gtk_action_group_add_action( mainActions, act );
2333 gtk_action_set_sensitive( act, TRUE );
2334 }
2335 }
2337 sigc::connection *connection = new sigc::connection(
2338 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2339 );
2340 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2341 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2342 }
2345 //########################
2346 //## Rect ##
2347 //########################
2349 static void sp_rtb_sensitivize( GObject *tbl )
2350 {
2351 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2352 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2353 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2355 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2356 gtk_action_set_sensitive( not_rounded, FALSE );
2357 } else {
2358 gtk_action_set_sensitive( not_rounded, TRUE );
2359 }
2360 }
2363 static void
2364 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2365 void (*setter)(SPRect *, gdouble))
2366 {
2367 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2369 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2370 SPUnit const *unit = tracker->getActiveUnit();
2372 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2373 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2374 }
2376 // quit if run by the attr_changed listener
2377 if (g_object_get_data( tbl, "freeze" )) {
2378 return;
2379 }
2381 // in turn, prevent listener from responding
2382 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2384 bool modmade = false;
2385 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2386 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2387 if (SP_IS_RECT(items->data)) {
2388 if (adj->value != 0) {
2389 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2390 } else {
2391 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2392 }
2393 modmade = true;
2394 }
2395 }
2397 sp_rtb_sensitivize( tbl );
2399 if (modmade) {
2400 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2401 _("Change rectangle"));
2402 }
2404 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2405 }
2407 static void
2408 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2409 {
2410 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2411 }
2413 static void
2414 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2415 {
2416 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2417 }
2419 static void
2420 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2421 {
2422 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2423 }
2425 static void
2426 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2427 {
2428 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2429 }
2433 static void
2434 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2435 {
2436 GtkAdjustment *adj = 0;
2438 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2439 gtk_adjustment_set_value(adj, 0.0);
2440 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2441 gtk_adjustment_value_changed(adj);
2443 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2444 gtk_adjustment_set_value(adj, 0.0);
2445 gtk_adjustment_value_changed(adj);
2447 sp_rtb_sensitivize( obj );
2448 }
2450 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2451 gchar const */*old_value*/, gchar const */*new_value*/,
2452 bool /*is_interactive*/, gpointer data)
2453 {
2454 GObject *tbl = G_OBJECT(data);
2456 // quit if run by the _changed callbacks
2457 if (g_object_get_data( tbl, "freeze" )) {
2458 return;
2459 }
2461 // in turn, prevent callbacks from responding
2462 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2464 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2465 SPUnit const *unit = tracker->getActiveUnit();
2467 gpointer item = g_object_get_data( tbl, "item" );
2468 if (item && SP_IS_RECT(item)) {
2469 {
2470 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2471 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2472 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2473 }
2475 {
2476 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2477 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2478 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2479 }
2481 {
2482 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2483 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2484 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2485 }
2487 {
2488 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2489 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2490 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2491 }
2492 }
2494 sp_rtb_sensitivize( tbl );
2496 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2497 }
2500 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2501 NULL, /* child_added */
2502 NULL, /* child_removed */
2503 rect_tb_event_attr_changed,
2504 NULL, /* content_changed */
2505 NULL /* order_changed */
2506 };
2508 /**
2509 * \param selection should not be NULL.
2510 */
2511 static void
2512 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2513 {
2514 int n_selected = 0;
2515 Inkscape::XML::Node *repr = NULL;
2516 SPItem *item = NULL;
2518 if ( g_object_get_data( tbl, "repr" ) ) {
2519 g_object_set_data( tbl, "item", NULL );
2520 }
2521 purge_repr_listener( tbl, tbl );
2523 for (GSList const *items = selection->itemList();
2524 items != NULL;
2525 items = items->next) {
2526 if (SP_IS_RECT((SPItem *) items->data)) {
2527 n_selected++;
2528 item = (SPItem *) items->data;
2529 repr = SP_OBJECT_REPR(item);
2530 }
2531 }
2533 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2535 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2537 if (n_selected == 0) {
2538 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2540 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2541 gtk_action_set_sensitive(w, FALSE);
2542 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2543 gtk_action_set_sensitive(h, FALSE);
2545 } else if (n_selected == 1) {
2546 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2547 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2549 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2550 gtk_action_set_sensitive(w, TRUE);
2551 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2552 gtk_action_set_sensitive(h, TRUE);
2554 if (repr) {
2555 g_object_set_data( tbl, "repr", repr );
2556 g_object_set_data( tbl, "item", item );
2557 Inkscape::GC::anchor(repr);
2558 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2559 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2560 }
2561 } else {
2562 // FIXME: implement averaging of all parameters for multiple selected
2563 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2564 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2565 sp_rtb_sensitivize( tbl );
2566 }
2567 }
2570 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2571 {
2572 EgeAdjustmentAction* eact = 0;
2573 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2575 {
2576 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2577 ege_output_action_set_use_markup( act, TRUE );
2578 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2579 g_object_set_data( holder, "mode_action", act );
2580 }
2582 // rx/ry units menu: create
2583 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2584 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2585 // fixme: add % meaning per cent of the width/height
2586 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2587 g_object_set_data( holder, "tracker", tracker );
2589 /* W */
2590 {
2591 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2592 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2593 eact = create_adjustment_action( "RectWidthAction",
2594 _("Width"), _("W:"), _("Width of rectangle"),
2595 "tools.shapes.rect", "width", 0,
2596 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2597 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2598 labels, values, G_N_ELEMENTS(labels),
2599 sp_rtb_width_value_changed );
2600 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2601 g_object_set_data( holder, "width_action", eact );
2602 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2603 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2604 }
2606 /* H */
2607 {
2608 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2609 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2610 eact = create_adjustment_action( "RectHeightAction",
2611 _("Height"), _("H:"), _("Height of rectangle"),
2612 "tools.shapes.rect", "height", 0,
2613 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2614 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2615 labels, values, G_N_ELEMENTS(labels),
2616 sp_rtb_height_value_changed );
2617 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2618 g_object_set_data( holder, "height_action", eact );
2619 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2620 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2621 }
2623 /* rx */
2624 {
2625 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2626 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2627 eact = create_adjustment_action( "RadiusXAction",
2628 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2629 "tools.shapes.rect", "rx", 0,
2630 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2631 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2632 labels, values, G_N_ELEMENTS(labels),
2633 sp_rtb_rx_value_changed);
2634 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2635 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2636 }
2638 /* ry */
2639 {
2640 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2641 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2642 eact = create_adjustment_action( "RadiusYAction",
2643 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2644 "tools.shapes.rect", "ry", 0,
2645 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2646 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2647 labels, values, G_N_ELEMENTS(labels),
2648 sp_rtb_ry_value_changed);
2649 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2650 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2651 }
2653 // add the units menu
2654 {
2655 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2656 gtk_action_group_add_action( mainActions, act );
2657 }
2659 /* Reset */
2660 {
2661 InkAction* inky = ink_action_new( "RectResetAction",
2662 _("Not rounded"),
2663 _("Make corners sharp"),
2664 "squared_corner",
2665 secondarySize );
2666 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2667 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2668 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2669 g_object_set_data( holder, "not_rounded", inky );
2670 }
2672 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2673 sp_rtb_sensitivize( holder );
2675 sigc::connection *connection = new sigc::connection(
2676 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2677 );
2678 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2679 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2680 }
2682 //########################
2683 //## 3D Box ##
2684 //########################
2686 // normalize angle so that it lies in the interval [0,360]
2687 static double box3d_normalize_angle (double a) {
2688 double angle = a + ((int) (a/360.0))*360;
2689 if (angle < 0) {
2690 angle += 360.0;
2691 }
2692 return angle;
2693 }
2695 static void
2696 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2697 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2698 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2699 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2700 // are reset).
2701 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2703 if (is_infinite) {
2704 gtk_toggle_action_set_active(tact, TRUE);
2705 gtk_action_set_sensitive(act, TRUE);
2707 double angle = persp3d_get_infinite_angle(persp, axis);
2708 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2709 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2710 }
2711 } else {
2712 gtk_toggle_action_set_active(tact, FALSE);
2713 gtk_action_set_sensitive(act, FALSE);
2714 }
2715 }
2717 static void
2718 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2719 if (!persp_repr) {
2720 g_print ("No perspective given to box3d_resync_toolbar().\n");
2721 return;
2722 }
2724 GtkWidget *tbl = GTK_WIDGET(data);
2725 GtkAdjustment *adj = 0;
2726 GtkAction *act = 0;
2727 GtkToggleAction *tact = 0;
2728 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2729 {
2730 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2731 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2732 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2734 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2735 }
2736 {
2737 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2738 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2739 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2741 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2742 }
2743 {
2744 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2745 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2746 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2748 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2749 }
2750 }
2752 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2753 gchar const */*old_value*/, gchar const */*new_value*/,
2754 bool /*is_interactive*/, gpointer data)
2755 {
2756 GtkWidget *tbl = GTK_WIDGET(data);
2758 // quit if run by the attr_changed listener
2759 // note: it used to work without the differently called freeze_ attributes (here and in
2760 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2761 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2762 return;
2763 }
2765 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2766 // sp_document_maybe_done() when the document is undo insensitive)
2767 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2769 // TODO: Only update the appropriate part of the toolbar
2770 // if (!strcmp(name, "inkscape:vp_z")) {
2771 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2772 // }
2774 Persp3D *persp = persp3d_get_from_repr(repr);
2775 persp3d_update_box_reprs(persp);
2777 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2778 }
2780 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2781 {
2782 NULL, /* child_added */
2783 NULL, /* child_removed */
2784 box3d_persp_tb_event_attr_changed,
2785 NULL, /* content_changed */
2786 NULL /* order_changed */
2787 };
2789 /**
2790 * \param selection Should not be NULL.
2791 */
2792 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2793 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2794 static void
2795 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2796 {
2797 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2798 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2799 // update the perspectives with infinite VPs and leave the other ones untouched).
2801 Inkscape::XML::Node *persp_repr = NULL;
2802 purge_repr_listener(tbl, tbl);
2804 SPItem *item = selection->singleItem();
2805 if (item && SP_IS_BOX3D(item)) {
2806 // FIXME: Also deal with multiple selected boxes
2807 SPBox3D *box = SP_BOX3D(item);
2808 Persp3D *persp = box3d_get_perspective(box);
2809 persp_repr = SP_OBJECT_REPR(persp);
2810 if (persp_repr) {
2811 g_object_set_data(tbl, "repr", persp_repr);
2812 Inkscape::GC::anchor(persp_repr);
2813 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2814 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2815 }
2817 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2818 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2820 box3d_resync_toolbar(persp_repr, tbl);
2821 }
2822 }
2824 static void
2825 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2826 {
2827 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2828 SPDocument *document = sp_desktop_document(desktop);
2830 // quit if run by the attr_changed listener
2831 // note: it used to work without the differently called freeze_ attributes (here and in
2832 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2833 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2834 return;
2835 }
2837 // in turn, prevent listener from responding
2838 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2840 //Persp3D *persp = document->current_persp3d;
2841 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2842 if (sel_persps.empty()) {
2843 // this can happen when the document is created; we silently ignore it
2844 return;
2845 }
2846 Persp3D *persp = sel_persps.front();
2848 persp->tmat.set_infinite_direction (axis, adj->value);
2849 SP_OBJECT(persp)->updateRepr();
2851 // TODO: use the correct axis here, too
2852 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2854 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2855 }
2858 static void
2859 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2860 {
2861 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2862 }
2864 static void
2865 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2866 {
2867 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2868 }
2870 static void
2871 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2872 {
2873 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2874 }
2877 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2878 {
2879 // TODO: Take all selected perspectives into account
2880 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2881 if (sel_persps.empty()) {
2882 // this can happen when the document is created; we silently ignore it
2883 return;
2884 }
2885 Persp3D *persp = sel_persps.front();
2887 bool set_infinite = gtk_toggle_action_get_active(act);
2888 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2889 }
2891 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2892 {
2893 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2894 }
2896 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2897 {
2898 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2899 }
2901 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2902 {
2903 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2904 }
2906 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2907 {
2908 EgeAdjustmentAction* eact = 0;
2909 SPDocument *document = sp_desktop_document (desktop);
2910 Persp3D *persp = document->current_persp3d;
2912 EgeAdjustmentAction* box3d_angle_x = 0;
2913 EgeAdjustmentAction* box3d_angle_y = 0;
2914 EgeAdjustmentAction* box3d_angle_z = 0;
2916 /* Angle X */
2917 {
2918 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2919 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2920 eact = create_adjustment_action( "3DBoxAngleXAction",
2921 _("Angle in X direction"), _("Angle X:"),
2922 // Translators: PL is short for 'perspective line'
2923 _("Angle of PLs in X direction"),
2924 "tools.shapes.3dbox", "box3d_angle_x", 30,
2925 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2926 -360.0, 360.0, 1.0, 10.0,
2927 labels, values, G_N_ELEMENTS(labels),
2928 box3d_angle_x_value_changed );
2929 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2930 g_object_set_data( holder, "box3d_angle_x_action", eact );
2931 box3d_angle_x = eact;
2932 }
2934 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2935 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2936 } else {
2937 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2938 }
2941 /* VP X state */
2942 {
2943 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2944 // Translators: VP is short for 'vanishing point'
2945 _("State of VP in X direction"),
2946 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2947 "toggle_vp_x",
2948 Inkscape::ICON_SIZE_DECORATION );
2949 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2950 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2951 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2952 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2953 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2954 }
2956 /* Angle Y */
2957 {
2958 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2959 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2960 eact = create_adjustment_action( "3DBoxAngleYAction",
2961 _("Angle in Y direction"), _("Angle Y:"),
2962 // Translators: PL is short for 'perspective line'
2963 _("Angle of PLs in Y direction"),
2964 "tools.shapes.3dbox", "box3d_angle_y", 30,
2965 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2966 -360.0, 360.0, 1.0, 10.0,
2967 labels, values, G_N_ELEMENTS(labels),
2968 box3d_angle_y_value_changed );
2969 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2970 g_object_set_data( holder, "box3d_angle_y_action", eact );
2971 box3d_angle_y = eact;
2972 }
2974 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2975 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2976 } else {
2977 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2978 }
2980 /* VP Y state */
2981 {
2982 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2983 // Translators: VP is short for 'vanishing point'
2984 _("State of VP in Y direction"),
2985 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2986 "toggle_vp_y",
2987 Inkscape::ICON_SIZE_DECORATION );
2988 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2989 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2990 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2991 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2992 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2993 }
2995 /* Angle Z */
2996 {
2997 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2998 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2999 eact = create_adjustment_action( "3DBoxAngleZAction",
3000 _("Angle in Z direction"), _("Angle Z:"),
3001 // Translators: PL is short for 'perspective line'
3002 _("Angle of PLs in Z direction"),
3003 "tools.shapes.3dbox", "box3d_angle_z", 30,
3004 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3005 -360.0, 360.0, 1.0, 10.0,
3006 labels, values, G_N_ELEMENTS(labels),
3007 box3d_angle_z_value_changed );
3008 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3009 g_object_set_data( holder, "box3d_angle_z_action", eact );
3010 box3d_angle_z = eact;
3011 }
3013 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3014 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3015 } else {
3016 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3017 }
3019 /* VP Z state */
3020 {
3021 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3022 // Translators: VP is short for 'vanishing point'
3023 _("State of VP in Z direction"),
3024 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3025 "toggle_vp_z",
3026 Inkscape::ICON_SIZE_DECORATION );
3027 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3028 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3029 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3030 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3031 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3032 }
3034 sigc::connection *connection = new sigc::connection(
3035 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3036 );
3037 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3038 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3039 }
3041 //########################
3042 //## Spiral ##
3043 //########################
3045 static void
3046 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3047 {
3048 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3050 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3051 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3052 }
3054 // quit if run by the attr_changed listener
3055 if (g_object_get_data( tbl, "freeze" )) {
3056 return;
3057 }
3059 // in turn, prevent listener from responding
3060 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3062 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3064 bool modmade = false;
3065 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3066 items != NULL;
3067 items = items->next)
3068 {
3069 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3070 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3071 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3072 SP_OBJECT((SPItem *) items->data)->updateRepr();
3073 modmade = true;
3074 }
3075 }
3077 g_free(namespaced_name);
3079 if (modmade) {
3080 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3081 _("Change spiral"));
3082 }
3084 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3085 }
3087 static void
3088 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3089 {
3090 sp_spl_tb_value_changed(adj, tbl, "revolution");
3091 }
3093 static void
3094 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3095 {
3096 sp_spl_tb_value_changed(adj, tbl, "expansion");
3097 }
3099 static void
3100 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3101 {
3102 sp_spl_tb_value_changed(adj, tbl, "t0");
3103 }
3105 static void
3106 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3107 {
3108 GtkWidget *tbl = GTK_WIDGET(obj);
3110 GtkAdjustment *adj;
3112 // fixme: make settable
3113 gdouble rev = 5;
3114 gdouble exp = 1.0;
3115 gdouble t0 = 0.0;
3117 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3118 gtk_adjustment_set_value(adj, rev);
3119 gtk_adjustment_value_changed(adj);
3121 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3122 gtk_adjustment_set_value(adj, exp);
3123 gtk_adjustment_value_changed(adj);
3125 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3126 gtk_adjustment_set_value(adj, t0);
3127 gtk_adjustment_value_changed(adj);
3129 spinbutton_defocus(GTK_OBJECT(tbl));
3130 }
3133 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3134 gchar const */*old_value*/, gchar const */*new_value*/,
3135 bool /*is_interactive*/, gpointer data)
3136 {
3137 GtkWidget *tbl = GTK_WIDGET(data);
3139 // quit if run by the _changed callbacks
3140 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3141 return;
3142 }
3144 // in turn, prevent callbacks from responding
3145 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3147 GtkAdjustment *adj;
3148 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3149 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3151 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3152 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3154 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3155 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3157 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3158 }
3161 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3162 NULL, /* child_added */
3163 NULL, /* child_removed */
3164 spiral_tb_event_attr_changed,
3165 NULL, /* content_changed */
3166 NULL /* order_changed */
3167 };
3169 static void
3170 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3171 {
3172 int n_selected = 0;
3173 Inkscape::XML::Node *repr = NULL;
3175 purge_repr_listener( tbl, tbl );
3177 for (GSList const *items = selection->itemList();
3178 items != NULL;
3179 items = items->next)
3180 {
3181 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3182 n_selected++;
3183 repr = SP_OBJECT_REPR((SPItem *) items->data);
3184 }
3185 }
3187 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3189 if (n_selected == 0) {
3190 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3191 } else if (n_selected == 1) {
3192 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3194 if (repr) {
3195 g_object_set_data( tbl, "repr", repr );
3196 Inkscape::GC::anchor(repr);
3197 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3198 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3199 }
3200 } else {
3201 // FIXME: implement averaging of all parameters for multiple selected
3202 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3203 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3204 }
3205 }
3208 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3209 {
3210 EgeAdjustmentAction* eact = 0;
3211 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3213 {
3214 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3215 ege_output_action_set_use_markup( act, TRUE );
3216 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3217 g_object_set_data( holder, "mode_action", act );
3218 }
3220 /* Revolution */
3221 {
3222 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3223 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3224 eact = create_adjustment_action( "SpiralRevolutionAction",
3225 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3226 "tools.shapes.spiral", "revolution", 3.0,
3227 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3228 0.01, 1024.0, 0.1, 1.0,
3229 labels, values, G_N_ELEMENTS(labels),
3230 sp_spl_tb_revolution_value_changed, 1, 2);
3231 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3232 }
3234 /* Expansion */
3235 {
3236 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3237 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3238 eact = create_adjustment_action( "SpiralExpansionAction",
3239 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3240 "tools.shapes.spiral", "expansion", 1.0,
3241 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3242 0.0, 1000.0, 0.01, 1.0,
3243 labels, values, G_N_ELEMENTS(labels),
3244 sp_spl_tb_expansion_value_changed);
3245 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3246 }
3248 /* T0 */
3249 {
3250 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3251 gdouble values[] = {0, 0.5, 0.9};
3252 eact = create_adjustment_action( "SpiralT0Action",
3253 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3254 "tools.shapes.spiral", "t0", 0.0,
3255 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3256 0.0, 0.999, 0.01, 1.0,
3257 labels, values, G_N_ELEMENTS(labels),
3258 sp_spl_tb_t0_value_changed);
3259 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3260 }
3262 /* Reset */
3263 {
3264 InkAction* inky = ink_action_new( "SpiralResetAction",
3265 _("Defaults"),
3266 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3267 GTK_STOCK_CLEAR,
3268 secondarySize );
3269 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3270 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3271 }
3274 sigc::connection *connection = new sigc::connection(
3275 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3276 );
3277 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3278 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3279 }
3281 //########################
3282 //## Pen/Pencil ##
3283 //########################
3285 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3286 static char const *
3287 freehand_tool_name(GObject *dataKludge)
3288 {
3289 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3290 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3291 ? "tools.freehand.pen"
3292 : "tools.freehand.pencil" );
3293 }
3295 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3296 {
3297 gint mode = ege_select_one_action_get_active(act);
3299 prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3301 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3303 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3304 // preparatory work here
3305 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3306 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3307 sp_pen_context_set_polyline_mode(pc);
3308 }
3309 }
3311 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3312 {
3313 /* Freehand mode toggle buttons */
3314 {
3315 guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3316 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3318 {
3319 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3321 GtkTreeIter iter;
3322 gtk_list_store_append( model, &iter );
3323 gtk_list_store_set( model, &iter,
3324 0, _("Bezier"),
3325 1, _("Create regular Bezier path"),
3326 2, "bezier_mode",
3327 -1 );
3329 gtk_list_store_append( model, &iter );
3330 gtk_list_store_set( model, &iter,
3331 0, _("Spiro"),
3332 1, _("Create Spiro path"),
3333 2, "spiro_splines_mode",
3334 -1 );
3336 if (!tool_is_pencil) {
3337 gtk_list_store_append( model, &iter );
3338 gtk_list_store_set( model, &iter,
3339 0, _("Zigzag"),
3340 1, _("Create a sequence of straight line segments"),
3341 2, "polylines_mode",
3342 -1 );
3344 gtk_list_store_append( model, &iter );
3345 gtk_list_store_set( model, &iter,
3346 0, _("Paraxial"),
3347 1, _("Create a sequence of paraxial line segments"),
3348 2, "paraxial_lines_mode",
3349 -1 );
3350 }
3352 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3353 "FreehandModeActionPencil" :
3354 "FreehandModeActionPen",
3355 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3356 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3358 ege_select_one_action_set_appearance( act, "full" );
3359 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3360 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3361 ege_select_one_action_set_icon_column( act, 2 );
3362 ege_select_one_action_set_icon_size( act, secondarySize );
3363 ege_select_one_action_set_tooltip_column( act, 1 );
3365 ege_select_one_action_set_active( act, freehandMode);
3366 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3367 }
3368 }
3369 }
3371 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3372 gint shape = ege_select_one_action_get_active( act );
3373 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3374 }
3376 /**
3377 * \brief Generate the list of freehand advanced shape option entries.
3378 */
3379 GList * freehand_shape_dropdown_items_list() {
3380 GList *glist = NULL;
3382 glist = g_list_append (glist, _("None"));
3383 glist = g_list_append (glist, _("Triangle in"));
3384 glist = g_list_append (glist, _("Triangle out"));
3385 glist = g_list_append (glist, _("Ellipse"));
3386 glist = g_list_append (glist, _("From clipboard"));
3388 return glist;
3389 }
3391 static void
3392 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3393 /*advanced shape options */
3394 {
3395 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3397 GList* items = 0;
3398 gint count = 0;
3399 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3400 {
3401 GtkTreeIter iter;
3402 gtk_list_store_append( model, &iter );
3403 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3404 count++;
3405 }
3406 g_list_free( items );
3407 items = 0;
3408 EgeSelectOneAction* act1 = ege_select_one_action_new(
3409 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3410 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3411 g_object_set( act1, "short_label", _("Shape:"), NULL );
3412 ege_select_one_action_set_appearance( act1, "compact" );
3413 ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3414 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3415 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3416 g_object_set_data( holder, "shape_action", act1 );
3417 }
3418 }
3420 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3421 {
3422 sp_add_freehand_mode_toggle(mainActions, holder, false);
3423 freehand_add_advanced_shape_options(mainActions, holder, false);
3424 }
3427 static void
3428 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3429 {
3430 GtkWidget *tbl = GTK_WIDGET(obj);
3432 GtkAdjustment *adj;
3434 // fixme: make settable
3435 gdouble tolerance = 4;
3437 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3438 gtk_adjustment_set_value(adj, tolerance);
3439 gtk_adjustment_value_changed(adj);
3441 spinbutton_defocus(GTK_OBJECT(tbl));
3442 }
3444 static void
3445 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3446 {
3448 // quit if run by the attr_changed listener
3449 if (g_object_get_data( tbl, "freeze" )) {
3450 return;
3451 }
3452 // in turn, prevent listener from responding
3453 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3454 prefs_set_double_attribute("tools.freehand.pencil",
3455 "tolerance", adj->value);
3456 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3458 }
3462 static void
3463 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3464 const gchar */*key*/,
3465 const gchar */*oldval*/,
3466 const gchar */*newval*/,
3467 bool /*is_interactive*/,
3468 void * data)
3469 {
3470 GObject* tbl = G_OBJECT(data);
3471 if (g_object_get_data( tbl, "freeze" )) {
3472 return;
3473 }
3475 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3477 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3478 "tolerance");
3480 double v = prefs_get_double_attribute("tools.freehand.pencil",
3481 "tolerance", adj->value);
3482 gtk_adjustment_set_value(adj, v);
3483 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3485 }
3487 static Inkscape::XML::NodeEventVector pencil_node_events =
3488 {
3489 NULL,
3490 NULL,
3491 sp_pencil_tb_tolerance_value_changed_external,
3492 NULL,
3493 NULL,
3494 };
3497 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3498 {
3499 sp_add_freehand_mode_toggle(mainActions, holder, true);
3501 EgeAdjustmentAction* eact = 0;
3503 /* Tolerance */
3504 {
3505 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3506 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3507 eact = create_adjustment_action( "PencilToleranceAction",
3508 _("Smoothing:"), _("Smoothing: "),
3509 _("How much smoothing (simplifying) is applied to the line"),
3510 "tools.freehand.pencil", "tolerance",
3511 3.0,
3512 GTK_WIDGET(desktop->canvas), NULL,
3513 holder, TRUE, "altx-pencil",
3514 1, 100.0, 0.5, 0,
3515 labels, values, G_N_ELEMENTS(labels),
3516 sp_pencil_tb_tolerance_value_changed,
3517 1, 2);
3518 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3519 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3521 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3522 "tools.freehand.pencil");
3523 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3524 g_object_set_data(G_OBJECT(holder), "repr", repr);
3526 }
3528 /* advanced shape options */
3529 freehand_add_advanced_shape_options(mainActions, holder, true);
3531 /* Reset */
3532 {
3533 InkAction* inky = ink_action_new( "PencilResetAction",
3534 _("Defaults"),
3535 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3536 GTK_STOCK_CLEAR,
3537 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3538 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3539 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3540 }
3542 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3544 }
3547 //########################
3548 //## Tweak ##
3549 //########################
3551 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3552 {
3553 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3554 }
3556 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3557 {
3558 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3559 }
3561 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3562 {
3563 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3564 }
3566 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3567 {
3568 int mode = ege_select_one_action_get_active( act );
3569 prefs_set_int_attribute("tools.tweak", "mode", mode);
3571 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3572 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3573 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3574 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3575 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3576 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3577 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3578 if (doh) gtk_action_set_sensitive (doh, TRUE);
3579 if (dos) gtk_action_set_sensitive (dos, TRUE);
3580 if (dol) gtk_action_set_sensitive (dol, TRUE);
3581 if (doo) gtk_action_set_sensitive (doo, TRUE);
3582 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3583 if (fid) gtk_action_set_sensitive (fid, FALSE);
3584 } else {
3585 if (doh) gtk_action_set_sensitive (doh, FALSE);
3586 if (dos) gtk_action_set_sensitive (dos, FALSE);
3587 if (dol) gtk_action_set_sensitive (dol, FALSE);
3588 if (doo) gtk_action_set_sensitive (doo, FALSE);
3589 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3590 if (fid) gtk_action_set_sensitive (fid, TRUE);
3591 }
3592 }
3594 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3595 {
3596 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3597 }
3599 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3600 bool show = gtk_toggle_action_get_active( act );
3601 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3602 }
3603 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3604 bool show = gtk_toggle_action_get_active( act );
3605 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3606 }
3607 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3608 bool show = gtk_toggle_action_get_active( act );
3609 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3610 }
3611 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3612 bool show = gtk_toggle_action_get_active( act );
3613 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3614 }
3616 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3617 {
3618 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3620 {
3621 /* Width */
3622 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3623 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3624 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3625 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3626 "tools.tweak", "width", 15,
3627 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3628 1, 100, 1.0, 0.0,
3629 labels, values, G_N_ELEMENTS(labels),
3630 sp_tweak_width_value_changed, 0.01, 0, 100 );
3631 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3632 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3633 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3634 }
3637 {
3638 /* Force */
3639 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3640 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3641 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3642 _("Force"), _("Force:"), _("The force of the tweak action"),
3643 "tools.tweak", "force", 20,
3644 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3645 1, 100, 1.0, 0.0,
3646 labels, values, G_N_ELEMENTS(labels),
3647 sp_tweak_force_value_changed, 0.01, 0, 100 );
3648 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3649 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3650 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3651 }
3653 /* Mode */
3654 {
3655 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3657 GtkTreeIter iter;
3658 gtk_list_store_append( model, &iter );
3659 gtk_list_store_set( model, &iter,
3660 0, _("Push mode"),
3661 1, _("Push parts of paths in any direction"),
3662 2, "tweak_push_mode",
3663 -1 );
3665 gtk_list_store_append( model, &iter );
3666 gtk_list_store_set( model, &iter,
3667 0, _("Shrink mode"),
3668 1, _("Shrink (inset) parts of paths"),
3669 2, "tweak_shrink_mode",
3670 -1 );
3672 gtk_list_store_append( model, &iter );
3673 gtk_list_store_set( model, &iter,
3674 0, _("Grow mode"),
3675 1, _("Grow (outset) parts of paths"),
3676 2, "tweak_grow_mode",
3677 -1 );
3679 gtk_list_store_append( model, &iter );
3680 gtk_list_store_set( model, &iter,
3681 0, _("Attract mode"),
3682 1, _("Attract parts of paths towards cursor"),
3683 2, "tweak_attract_mode",
3684 -1 );
3686 gtk_list_store_append( model, &iter );
3687 gtk_list_store_set( model, &iter,
3688 0, _("Repel mode"),
3689 1, _("Repel parts of paths from cursor"),
3690 2, "tweak_repel_mode",
3691 -1 );
3693 gtk_list_store_append( model, &iter );
3694 gtk_list_store_set( model, &iter,
3695 0, _("Roughen mode"),
3696 1, _("Roughen parts of paths"),
3697 2, "tweak_roughen_mode",
3698 -1 );
3700 gtk_list_store_append( model, &iter );
3701 gtk_list_store_set( model, &iter,
3702 0, _("Color paint mode"),
3703 1, _("Paint the tool's color upon selected objects"),
3704 2, "tweak_colorpaint_mode",
3705 -1 );
3707 gtk_list_store_append( model, &iter );
3708 gtk_list_store_set( model, &iter,
3709 0, _("Color jitter mode"),
3710 1, _("Jitter the colors of selected objects"),
3711 2, "tweak_colorjitter_mode",
3712 -1 );
3714 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3715 g_object_set( act, "short_label", _("Mode:"), NULL );
3716 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3717 g_object_set_data( holder, "mode_action", act );
3719 ege_select_one_action_set_appearance( act, "full" );
3720 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3721 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3722 ege_select_one_action_set_icon_column( act, 2 );
3723 ege_select_one_action_set_icon_size( act, secondarySize );
3724 ege_select_one_action_set_tooltip_column( act, 1 );
3726 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3727 ege_select_one_action_set_active( act, mode );
3728 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3730 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3731 }
3733 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3735 {
3736 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3737 ege_output_action_set_use_markup( act, TRUE );
3738 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3739 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3740 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3741 g_object_set_data( holder, "tweak_channels_label", act);
3742 }
3744 {
3745 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3746 _("Hue"),
3747 _("In color mode, act on objects' hue"),
3748 NULL,
3749 Inkscape::ICON_SIZE_DECORATION );
3750 //TRANSLATORS: "H" here stands for hue
3751 g_object_set( act, "short_label", _("H"), NULL );
3752 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3753 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3754 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3755 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3756 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3757 g_object_set_data( holder, "tweak_doh", act);
3758 }
3759 {
3760 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3761 _("Saturation"),
3762 _("In color mode, act on objects' saturation"),
3763 NULL,
3764 Inkscape::ICON_SIZE_DECORATION );
3765 //TRANSLATORS: "S" here stands for Saturation
3766 g_object_set( act, "short_label", _("S"), NULL );
3767 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3768 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3769 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3770 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3771 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3772 g_object_set_data( holder, "tweak_dos", act );
3773 }
3774 {
3775 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3776 _("Lightness"),
3777 _("In color mode, act on objects' lightness"),
3778 NULL,
3779 Inkscape::ICON_SIZE_DECORATION );
3780 //TRANSLATORS: "L" here stands for Lightness
3781 g_object_set( act, "short_label", _("L"), NULL );
3782 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3783 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3784 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3785 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3786 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3787 g_object_set_data( holder, "tweak_dol", act );
3788 }
3789 {
3790 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3791 _("Opacity"),
3792 _("In color mode, act on objects' opacity"),
3793 NULL,
3794 Inkscape::ICON_SIZE_DECORATION );
3795 //TRANSLATORS: "O" here stands for Opacity
3796 g_object_set( act, "short_label", _("O"), NULL );
3797 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3798 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3799 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3800 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3801 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3802 g_object_set_data( holder, "tweak_doo", act );
3803 }
3805 { /* Fidelity */
3806 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3807 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3808 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3809 _("Fidelity"), _("Fidelity:"),
3810 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3811 "tools.tweak", "fidelity", 50,
3812 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3813 1, 100, 1.0, 10.0,
3814 labels, values, G_N_ELEMENTS(labels),
3815 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3816 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3817 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3818 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3819 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3820 g_object_set_data( holder, "tweak_fidelity", eact );
3821 }
3824 /* Use Pressure button */
3825 {
3826 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3827 _("Pressure"),
3828 _("Use the pressure of the input device to alter the force of tweak action"),
3829 "use_pressure",
3830 Inkscape::ICON_SIZE_DECORATION );
3831 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3832 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3833 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3834 }
3836 }
3839 //########################
3840 //## Calligraphy ##
3841 //########################
3842 static void update_presets_list (GObject *tbl)
3843 {
3844 if (g_object_get_data(tbl, "presets_blocked"))
3845 return;
3847 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3848 if (!sel) {
3849 ege_select_one_action_set_active(sel, 0);
3850 return;
3851 }
3853 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3855 for (int i = 1; i <= total_prefs; i++) {
3856 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3857 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3859 bool match = true;
3861 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3862 iter;
3863 ++iter ) {
3864 const gchar *attr_name = g_quark_to_string(iter->key);
3865 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3866 continue;
3867 void *widget = g_object_get_data(tbl, attr_name);
3868 if (widget) {
3869 if (GTK_IS_ADJUSTMENT(widget)) {
3870 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3871 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3872 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3873 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3874 match = false;
3875 break;
3876 }
3877 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3878 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3879 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3880 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3881 if (gtk_toggle_action_get_active(toggle) != v) {
3882 match = false;
3883 break;
3884 }
3885 }
3886 }
3887 }
3889 if (match) {
3890 // newly added item is at the same index as the
3891 // save command, so we need to change twice for it to take effect
3892 ege_select_one_action_set_active(sel, 0);
3893 ege_select_one_action_set_active(sel, i);
3894 return;
3895 }
3896 }
3898 // no match found
3899 ege_select_one_action_set_active(sel, 0);
3900 }
3902 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3903 {
3904 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3905 update_presets_list(tbl);
3906 }
3908 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3909 {
3910 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3911 update_presets_list(tbl);
3912 }
3914 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3915 {
3916 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3917 update_presets_list(tbl);
3918 }
3920 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3921 {
3922 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3923 update_presets_list(tbl);
3924 }
3926 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3927 {
3928 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3929 update_presets_list(tbl);
3930 }
3932 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3933 {
3934 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3935 update_presets_list(tbl);
3936 }
3938 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3939 {
3940 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3941 update_presets_list(tbl);
3942 }
3944 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3945 {
3946 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3947 update_presets_list(tbl);
3948 }
3950 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3951 {
3952 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3953 update_presets_list(tbl);
3954 }
3956 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3957 {
3958 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3959 update_presets_list(tbl);
3960 }
3962 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3963 {
3964 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3965 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3966 update_presets_list(tbl);
3967 if (calligraphy_angle )
3968 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3969 }
3972 static gchar const *const widget_names[] = {
3973 "width",
3974 "mass",
3975 "wiggle",
3976 "angle",
3977 "thinning",
3978 "tremor",
3979 "flatness",
3980 "cap_rounding",
3981 "usepressure",
3982 "tracebackground",
3983 "usetilt"
3984 };
3987 static void sp_dcc_build_presets_list(GObject *tbl)
3988 {
3989 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3991 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3992 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3993 gtk_list_store_clear (model);
3995 {
3996 GtkTreeIter iter;
3997 gtk_list_store_append( model, &iter );
3998 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3999 }
4001 //TODO: switch back to prefs API
4002 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4003 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4004 int ii=1;
4005 while (child_repr) {
4006 GtkTreeIter iter;
4007 char *preset_name = (char *) child_repr->attribute("name");
4008 gtk_list_store_append( model, &iter );
4009 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4010 child_repr = sp_repr_next(child_repr);
4011 }
4013 {
4014 GtkTreeIter iter;
4015 gtk_list_store_append( model, &iter );
4016 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4017 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4018 }
4020 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4022 update_presets_list (tbl);
4023 }
4025 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4026 {
4027 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4028 if (! desktop) return;
4030 if (g_object_get_data(tbl, "presets_blocked"))
4031 return;
4033 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4034 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4035 // dialog cancelled
4036 update_presets_list (tbl);
4037 return;
4038 }
4039 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4041 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4042 // empty name entered
4043 update_presets_list (tbl);
4044 return;
4045 }
4047 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4049 int new_index = -1;
4050 gchar *pref_path = NULL;
4051 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4053 for (int i = 1; i <= total_prefs; i++) {
4054 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4055 const gchar *name = prefs_get_string_attribute(path, "name");
4056 if (name && !strcmp(name, profile_name.c_str())) {
4057 // we already have preset with this name, replace its values
4058 new_index = i;
4059 pref_path = g_strdup(path);
4060 break;
4061 }
4062 }
4064 if (new_index == -1) {
4065 // no preset with this name, create
4066 new_index = total_prefs + 1;
4067 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4068 pref_path = create_pref("tools.calligraphic.preset", profile_id);
4069 g_free(profile_id);
4070 }
4072 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4073 gchar const *const widget_name = widget_names[i];
4074 void *widget = g_object_get_data(tbl, widget_name);
4075 if (widget) {
4076 if (GTK_IS_ADJUSTMENT(widget)) {
4077 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4078 double v = gtk_adjustment_get_value(adj);
4079 prefs_set_double_attribute(pref_path, widget_name, v);
4080 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4081 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4082 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4083 int v = gtk_toggle_action_get_active(toggle);
4084 prefs_set_int_attribute(pref_path, widget_name, v);
4085 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4086 } else {
4087 g_warning("Unknown widget type for preset: %s\n", widget_name);
4088 }
4089 } else {
4090 g_warning("Bad key when writing preset: %s\n", widget_name);
4091 }
4092 }
4093 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4095 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4097 sp_dcc_build_presets_list (tbl);
4099 g_free(pref_path);
4100 }
4103 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4105 gint preset_index = ege_select_one_action_get_active( act );
4106 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4108 if (preset_index == save_presets_index) {
4109 // this is the Save command
4110 sp_dcc_save_profile(NULL, tbl);
4111 return;
4112 }
4114 if (g_object_get_data(tbl, "presets_blocked"))
4115 return;
4117 gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4119 if (preset_path) {
4120 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE)); //temporarily block the selector so no one will updadte it while we're reading it
4122 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4124 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4125 iter;
4126 ++iter ) {
4127 const gchar *attr_name = g_quark_to_string(iter->key);
4128 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4129 continue;
4130 void *widget = g_object_get_data(tbl, attr_name);
4131 if (widget) {
4132 if (GTK_IS_ADJUSTMENT(widget)) {
4133 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4134 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4135 gtk_adjustment_set_value(adj, v);
4136 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4137 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4138 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4139 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4140 gtk_toggle_action_set_active(toggle, v);
4141 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4142 } else {
4143 g_warning("Unknown widget type for preset: %s\n", attr_name);
4144 }
4145 } else {
4146 g_warning("Bad key found in a preset record: %s\n", attr_name);
4147 }
4148 }
4149 g_free(preset_path);
4150 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4151 }
4153 }
4156 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4157 {
4158 {
4159 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4161 EgeAdjustmentAction* calligraphy_angle = 0;
4163 {
4164 /* Width */
4165 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4166 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4167 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4168 _("Pen Width"), _("Width:"),
4169 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4170 "tools.calligraphic", "width", 15,
4171 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4172 1, 100, 1.0, 0.0,
4173 labels, values, G_N_ELEMENTS(labels),
4174 sp_ddc_width_value_changed, 0.01, 0, 100 );
4175 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4176 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4177 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4178 }
4180 {
4181 /* Thinning */
4182 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4183 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4184 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4185 _("Stroke Thinning"), _("Thinning:"),
4186 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4187 "tools.calligraphic", "thinning", 10,
4188 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4189 -100, 100, 1, 0.1,
4190 labels, values, G_N_ELEMENTS(labels),
4191 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4192 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4193 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4194 }
4196 {
4197 /* Angle */
4198 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4199 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4200 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4201 _("Pen Angle"), _("Angle:"),
4202 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4203 "tools.calligraphic", "angle", 30,
4204 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4205 -90.0, 90.0, 1.0, 10.0,
4206 labels, values, G_N_ELEMENTS(labels),
4207 sp_ddc_angle_value_changed, 1, 0 );
4208 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4209 g_object_set_data( holder, "angle_action", eact );
4210 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4211 calligraphy_angle = eact;
4212 }
4214 {
4215 /* Fixation */
4216 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4217 gdouble values[] = {0, 20, 40, 60, 90, 100};
4218 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4219 _("Fixation"), _("Fixation:"),
4220 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4221 "tools.calligraphic", "flatness", 90,
4222 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4223 0.0, 100, 1.0, 10.0,
4224 labels, values, G_N_ELEMENTS(labels),
4225 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4226 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4227 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4228 }
4230 {
4231 /* Cap Rounding */
4232 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4233 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4234 // TRANSLATORS: "cap" means "end" (both start and finish) here
4235 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4236 _("Cap rounding"), _("Caps:"),
4237 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4238 "tools.calligraphic", "cap_rounding", 0.0,
4239 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4240 0.0, 5.0, 0.01, 0.1,
4241 labels, values, G_N_ELEMENTS(labels),
4242 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4243 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4244 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4245 }
4247 {
4248 /* Tremor */
4249 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4250 gdouble values[] = {0, 10, 20, 40, 60, 100};
4251 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4252 _("Stroke Tremor"), _("Tremor:"),
4253 _("Increase to make strokes rugged and trembling"),
4254 "tools.calligraphic", "tremor", 0.0,
4255 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4256 0.0, 100, 1, 0.0,
4257 labels, values, G_N_ELEMENTS(labels),
4258 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4260 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4261 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4262 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4263 }
4265 {
4266 /* Wiggle */
4267 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4268 gdouble values[] = {0, 20, 40, 60, 100};
4269 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4270 _("Pen Wiggle"), _("Wiggle:"),
4271 _("Increase to make the pen waver and wiggle"),
4272 "tools.calligraphic", "wiggle", 0.0,
4273 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4274 0.0, 100, 1, 0.0,
4275 labels, values, G_N_ELEMENTS(labels),
4276 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4277 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4278 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4279 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4280 }
4282 {
4283 /* Mass */
4284 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4285 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4286 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4287 _("Pen Mass"), _("Mass:"),
4288 _("Increase to make the pen drag behind, as if slowed by inertia"),
4289 "tools.calligraphic", "mass", 2.0,
4290 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4291 0.0, 100, 1, 0.0,
4292 labels, values, G_N_ELEMENTS(labels),
4293 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4294 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4295 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4296 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4297 }
4300 /* Trace Background button */
4301 {
4302 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4303 _("Trace Background"),
4304 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4305 "trace_background",
4306 Inkscape::ICON_SIZE_DECORATION );
4307 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4308 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4309 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4310 g_object_set_data( holder, "tracebackground", act );
4311 }
4313 /* Use Pressure button */
4314 {
4315 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4316 _("Pressure"),
4317 _("Use the pressure of the input device to alter the width of the pen"),
4318 "use_pressure",
4319 Inkscape::ICON_SIZE_DECORATION );
4320 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4321 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4322 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4323 g_object_set_data( holder, "usepressure", act );
4324 }
4326 /* Use Tilt button */
4327 {
4328 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4329 _("Tilt"),
4330 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4331 "use_tilt",
4332 Inkscape::ICON_SIZE_DECORATION );
4333 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4334 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4335 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4336 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4337 g_object_set_data( holder, "usetilt", act );
4338 }
4340 /*calligraphic profile */
4341 {
4342 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4343 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4344 ege_select_one_action_set_appearance (act1, "compact");
4345 g_object_set_data (holder, "profile_selector", act1 );
4347 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4349 sp_dcc_build_presets_list (holder);
4351 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4352 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4353 }
4354 }
4355 }
4358 //########################
4359 //## Circle / Arc ##
4360 //########################
4362 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4363 {
4364 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4365 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4367 if (v1 == 0 && v2 == 0) {
4368 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4369 gtk_action_set_sensitive( ocb, FALSE );
4370 gtk_action_set_sensitive( make_whole, FALSE );
4371 }
4372 } else {
4373 gtk_action_set_sensitive( ocb, TRUE );
4374 gtk_action_set_sensitive( make_whole, TRUE );
4375 }
4376 }
4378 static void
4379 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4380 {
4381 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4383 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4384 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4385 }
4387 // quit if run by the attr_changed listener
4388 if (g_object_get_data( tbl, "freeze" )) {
4389 return;
4390 }
4392 // in turn, prevent listener from responding
4393 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4395 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4397 bool modmade = false;
4398 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4399 items != NULL;
4400 items = items->next)
4401 {
4402 SPItem *item = SP_ITEM(items->data);
4404 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4406 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4407 SPArc *arc = SP_ARC(item);
4409 if (!strcmp(value_name, "start"))
4410 ge->start = (adj->value * M_PI)/ 180;
4411 else
4412 ge->end = (adj->value * M_PI)/ 180;
4414 sp_genericellipse_normalize(ge);
4415 ((SPObject *)arc)->updateRepr();
4416 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4418 modmade = true;
4419 }
4420 }
4422 g_free(namespaced_name);
4424 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4426 sp_arctb_sensitivize( tbl, adj->value, other->value );
4428 if (modmade) {
4429 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4430 _("Arc: Change start/end"));
4431 }
4433 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4434 }
4437 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4438 {
4439 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4440 }
4442 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4443 {
4444 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4445 }
4448 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4449 {
4450 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4451 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4452 if ( ege_select_one_action_get_active( act ) != 0 ) {
4453 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4454 } else {
4455 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4456 }
4457 }
4459 // quit if run by the attr_changed listener
4460 if (g_object_get_data( tbl, "freeze" )) {
4461 return;
4462 }
4464 // in turn, prevent listener from responding
4465 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4467 bool modmade = false;
4469 if ( ege_select_one_action_get_active(act) != 0 ) {
4470 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4471 items != NULL;
4472 items = items->next)
4473 {
4474 if (SP_IS_ARC((SPItem *) items->data)) {
4475 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4476 repr->setAttribute("sodipodi:open", "true");
4477 SP_OBJECT((SPItem *) items->data)->updateRepr();
4478 modmade = true;
4479 }
4480 }
4481 } else {
4482 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4483 items != NULL;
4484 items = items->next)
4485 {
4486 if (SP_IS_ARC((SPItem *) items->data)) {
4487 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4488 repr->setAttribute("sodipodi:open", NULL);
4489 SP_OBJECT((SPItem *) items->data)->updateRepr();
4490 modmade = true;
4491 }
4492 }
4493 }
4495 if (modmade) {
4496 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4497 _("Arc: Change open/closed"));
4498 }
4500 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4501 }
4503 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4504 {
4505 GtkAdjustment *adj;
4506 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4507 gtk_adjustment_set_value(adj, 0.0);
4508 gtk_adjustment_value_changed(adj);
4510 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4511 gtk_adjustment_set_value(adj, 0.0);
4512 gtk_adjustment_value_changed(adj);
4514 spinbutton_defocus( GTK_OBJECT(obj) );
4515 }
4517 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4518 gchar const */*old_value*/, gchar const */*new_value*/,
4519 bool /*is_interactive*/, gpointer data)
4520 {
4521 GObject *tbl = G_OBJECT(data);
4523 // quit if run by the _changed callbacks
4524 if (g_object_get_data( tbl, "freeze" )) {
4525 return;
4526 }
4528 // in turn, prevent callbacks from responding
4529 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4531 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4532 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4534 GtkAdjustment *adj1,*adj2;
4535 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4536 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4537 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4538 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4540 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4542 char const *openstr = NULL;
4543 openstr = repr->attribute("sodipodi:open");
4544 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4546 if (openstr) {
4547 ege_select_one_action_set_active( ocb, 1 );
4548 } else {
4549 ege_select_one_action_set_active( ocb, 0 );
4550 }
4552 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4553 }
4555 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4556 NULL, /* child_added */
4557 NULL, /* child_removed */
4558 arc_tb_event_attr_changed,
4559 NULL, /* content_changed */
4560 NULL /* order_changed */
4561 };
4564 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4565 {
4566 int n_selected = 0;
4567 Inkscape::XML::Node *repr = NULL;
4569 purge_repr_listener( tbl, tbl );
4571 for (GSList const *items = selection->itemList();
4572 items != NULL;
4573 items = items->next)
4574 {
4575 if (SP_IS_ARC((SPItem *) items->data)) {
4576 n_selected++;
4577 repr = SP_OBJECT_REPR((SPItem *) items->data);
4578 }
4579 }
4581 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4583 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4584 if (n_selected == 0) {
4585 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4586 } else if (n_selected == 1) {
4587 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4588 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4590 if (repr) {
4591 g_object_set_data( tbl, "repr", repr );
4592 Inkscape::GC::anchor(repr);
4593 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4594 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4595 }
4596 } else {
4597 // FIXME: implement averaging of all parameters for multiple selected
4598 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4599 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4600 sp_arctb_sensitivize( tbl, 1, 0 );
4601 }
4602 }
4605 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4606 {
4607 EgeAdjustmentAction* eact = 0;
4608 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4611 {
4612 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4613 ege_output_action_set_use_markup( act, TRUE );
4614 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4615 g_object_set_data( holder, "mode_action", act );
4616 }
4618 /* Start */
4619 {
4620 eact = create_adjustment_action( "ArcStartAction",
4621 _("Start"), _("Start:"),
4622 _("The angle (in degrees) from the horizontal to the arc's start point"),
4623 "tools.shapes.arc", "start", 0.0,
4624 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4625 -360.0, 360.0, 1.0, 10.0,
4626 0, 0, 0,
4627 sp_arctb_start_value_changed);
4628 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4629 }
4631 /* End */
4632 {
4633 eact = create_adjustment_action( "ArcEndAction",
4634 _("End"), _("End:"),
4635 _("The angle (in degrees) from the horizontal to the arc's end point"),
4636 "tools.shapes.arc", "end", 0.0,
4637 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4638 -360.0, 360.0, 1.0, 10.0,
4639 0, 0, 0,
4640 sp_arctb_end_value_changed);
4641 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4642 }
4644 /* Segments / Pie checkbox */
4645 {
4646 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4648 GtkTreeIter iter;
4649 gtk_list_store_append( model, &iter );
4650 gtk_list_store_set( model, &iter,
4651 0, _("Closed arc"),
4652 1, _("Switch to segment (closed shape with two radii)"),
4653 2, "circle_closed_arc",
4654 -1 );
4656 gtk_list_store_append( model, &iter );
4657 gtk_list_store_set( model, &iter,
4658 0, _("Open Arc"),
4659 1, _("Switch to arc (unclosed shape)"),
4660 2, "circle_open_arc",
4661 -1 );
4663 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4664 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4665 g_object_set_data( holder, "open_action", act );
4667 ege_select_one_action_set_appearance( act, "full" );
4668 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4669 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4670 ege_select_one_action_set_icon_column( act, 2 );
4671 ege_select_one_action_set_icon_size( act, secondarySize );
4672 ege_select_one_action_set_tooltip_column( act, 1 );
4674 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4675 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4676 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4677 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4678 }
4680 /* Make Whole */
4681 {
4682 InkAction* inky = ink_action_new( "ArcResetAction",
4683 _("Make whole"),
4684 _("Make the shape a whole ellipse, not arc or segment"),
4685 "reset_circle",
4686 secondarySize );
4687 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4688 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4689 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4690 g_object_set_data( holder, "make_whole", inky );
4691 }
4693 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4694 // sensitivize make whole and open checkbox
4695 {
4696 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4697 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4698 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4699 }
4702 sigc::connection *connection = new sigc::connection(
4703 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4704 );
4705 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4706 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4707 }
4712 // toggle button callbacks and updaters
4714 //########################
4715 //## Dropper ##
4716 //########################
4718 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4719 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4720 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4721 if ( set_action ) {
4722 if ( gtk_toggle_action_get_active( act ) ) {
4723 gtk_action_set_sensitive( set_action, TRUE );
4724 } else {
4725 gtk_action_set_sensitive( set_action, FALSE );
4726 }
4727 }
4729 spinbutton_defocus(GTK_OBJECT(tbl));
4730 }
4732 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4733 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4734 spinbutton_defocus(GTK_OBJECT(tbl));
4735 }
4738 /**
4739 * Dropper auxiliary toolbar construction and setup.
4740 *
4741 * TODO: Would like to add swatch of current color.
4742 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4743 * can drag and drop places. Will provide a nice mixing palette.
4744 */
4745 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4746 {
4747 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4749 {
4750 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4751 ege_output_action_set_use_markup( act, TRUE );
4752 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4753 }
4755 {
4756 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4757 _("Pick opacity"),
4758 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4759 NULL,
4760 Inkscape::ICON_SIZE_DECORATION );
4761 g_object_set( act, "short_label", _("Pick"), NULL );
4762 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4763 g_object_set_data( holder, "pick_action", act );
4764 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4765 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4766 }
4768 {
4769 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4770 _("Assign opacity"),
4771 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4772 NULL,
4773 Inkscape::ICON_SIZE_DECORATION );
4774 g_object_set( act, "short_label", _("Assign"), NULL );
4775 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4776 g_object_set_data( holder, "set_action", act );
4777 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4778 // make sure it's disabled if we're not picking alpha
4779 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4780 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4781 }
4782 }
4785 //########################
4786 //## LPETool ##
4787 //########################
4789 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4791 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4792 {
4793 using namespace Inkscape::LivePathEffect;
4795 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4797 // TODO: how can we set *all* actions inactive (such that no sutool is activated?)
4798 gint lpeToolMode = ege_select_one_action_get_active(act);
4799 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4800 prefs_set_int_attribute( "tools.lpetool", "mode", lpeToolMode );
4801 }
4802 //EffectType type = lpesubtools[lpeToolMode];
4803 //SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
4805 // only take action if run by the attr_changed listener
4806 if (!g_object_get_data( tbl, "freeze" )) {
4807 // in turn, prevent listener from responding
4808 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4810 // this is now done in sp_lpetool_context_root_handler()
4811 /**
4812 // activate the LPE corresponding to the chosen subtool
4813 if (type != INVALID_LPE) {
4814 //lc->tool_state = LPETOOL_STATE_PEN;
4815 sp_pen_context_wait_for_LPE_mouse_clicks(pc, type, Effect::acceptsNumClicks(type));
4816 }
4817 // TODO: how can we take LPEs into account that don't expect any 'pre-clicks'?
4818 **/
4820 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4821 }
4822 }
4824 static void
4825 sp_lpetool_test_value_changed(GtkAdjustment *adj, GObject *tbl)
4826 {
4827 g_print ("sp_lpetool_test_value_changed()\n");
4828 using namespace Inkscape::LivePathEffect;
4830 // quit if run by the attr_changed listener
4831 if (g_object_get_data( tbl, "freeze" )) {
4832 return;
4833 }
4835 // in turn, prevent listener from responding
4836 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
4838 LPEAngleBisector *lpeab = static_cast<LPEAngleBisector *>(g_object_get_data(tbl, "currentlpe"));
4839 if (!lpeab) {
4840 g_print ("no LPE!\n");
4841 } else {
4842 g_print ("LPE found. Adjusting left length\n");
4843 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4844 lpeab->length_left.param_set_value(gtk_adjustment_get_value(adj));
4845 sp_lpe_item_update_patheffect(lpeitem, true, true);
4846 }
4848 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4849 }
4851 void
4852 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4853 {
4854 using namespace Inkscape::LivePathEffect;
4855 g_print ("sp_lpetool_toolbox_sel_changed()");
4856 {
4857 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "lpetool_test_action" ) );
4858 SPItem *item = selection->singleItem();
4859 if (item && SP_IS_LPE_ITEM(item)) {
4860 g_print (" - item found\n");
4861 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4862 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4863 if (lpe && lpe->effectType() == ANGLE_BISECTOR) {
4864 LPEAngleBisector *lpeab = dynamic_cast<LPEAngleBisector*>(lpe);
4865 g_object_set_data(tbl, "currentlpe", lpeab);
4866 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4867 gtk_action_set_sensitive(w, TRUE);
4868 } else {
4869 g_object_set_data(tbl, "currentlpe", NULL);
4870 g_object_set_data(tbl, "currentlpeitem", NULL);
4871 gtk_action_set_sensitive(w, FALSE);
4872 }
4873 } else {
4874 g_print (" - unsetting item\n");
4875 g_object_set_data(tbl, "currentlpe", NULL);
4876 g_object_set_data(tbl, "currentlpeitem", NULL);
4877 gtk_action_set_sensitive(w, FALSE);
4878 }
4879 }
4880 g_print ("\n");
4881 }
4883 static void
4884 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4885 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4887 bool show = gtk_toggle_action_get_active( act );
4888 prefs_set_int_attribute ("tools.lpetool", "show_bbox", show ? 1 : 0);
4890 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4891 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4892 lpetool_context_reset_limiting_bbox(lc);
4893 }
4894 }
4896 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4897 {
4898 /** Automatically create a list of LPEs that get added to the toolbar **/
4899 {
4900 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4902 GtkTreeIter iter;
4904 // the first toggle button represents the state that no subtool is active (remove this when
4905 // this can be modeled by EgeSelectOneAction or some other action)
4906 gtk_list_store_append( model, &iter );
4907 gtk_list_store_set( model, &iter,
4908 0, _("All inactive"),
4909 1, _("No geometric tool is active"),
4910 2, _("all_inactive"),
4911 -1 );
4913 Inkscape::LivePathEffect::EffectType type;
4914 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
4915 type = lpesubtools[i];
4916 gtk_list_store_append( model, &iter );
4917 gtk_list_store_set( model, &iter,
4918 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4919 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4920 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4921 -1 );
4922 }
4924 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4925 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4926 g_object_set_data( holder, "lpetool_mode_action", act );
4928 ege_select_one_action_set_appearance( act, "full" );
4929 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4930 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4931 ege_select_one_action_set_icon_column( act, 2 );
4932 ege_select_one_action_set_tooltip_column( act, 1 );
4934 gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4935 ege_select_one_action_set_active( act, lpeToolMode );
4936 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4937 }
4939 /* Show limiting bounding box */
4940 {
4941 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
4942 _("Show limiting bounding box"),
4943 _("Show bounding box (is used to cut infinite lines)"),
4944 "lpetool_show_bbox",
4945 Inkscape::ICON_SIZE_DECORATION );
4946 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4947 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
4948 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
4949 }
4951 /* Test action */
4952 /**
4953 {
4954 EgeAdjustmentAction* eact = 0;
4955 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
4956 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
4957 eact = create_adjustment_action( "TestLPEAction",
4958 _("Test value"), _("Test value:"), _("Test for interactive control widgets ..."),
4959 "tools.lpetool", "testvalue", 0,
4960 GTK_WIDGET(desktop->canvas), NULL us, holder, TRUE, "altx-lpetool",
4961 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
4962 labels, values, G_N_ELEMENTS(labels),
4963 sp_lpetool_test_value_changed );
4964 //tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4965 g_object_set_data( holder, "lpetool_test_action", eact );
4966 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
4967 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4968 }
4969 **/
4971 //watch selection
4972 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
4974 sigc::connection *c_selection_changed =
4975 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4976 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
4977 pool->add_connection ("selection-changed", c_selection_changed);
4978 }
4980 //########################
4981 //## Eraser ##
4982 //########################
4984 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4985 {
4986 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4987 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4988 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4989 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4990 }
4992 // only take action if run by the attr_changed listener
4993 if (!g_object_get_data( tbl, "freeze" )) {
4994 // in turn, prevent listener from responding
4995 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4997 if ( eraserMode != 0 ) {
4998 } else {
4999 }
5000 // TODO finish implementation
5002 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5003 }
5004 }
5006 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5007 {
5008 {
5009 /* Width */
5010 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5011 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5012 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5013 _("Pen Width"), _("Width:"),
5014 _("The width of the eraser pen (relative to the visible canvas area)"),
5015 "tools.eraser", "width", 15,
5016 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5017 1, 100, 1.0, 0.0,
5018 labels, values, G_N_ELEMENTS(labels),
5019 sp_ddc_width_value_changed, 0.01, 0, 100 );
5020 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5021 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5022 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5023 }
5025 {
5026 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5028 GtkTreeIter iter;
5029 gtk_list_store_append( model, &iter );
5030 gtk_list_store_set( model, &iter,
5031 0, _("Delete"),
5032 1, _("Delete objects touched by the eraser"),
5033 2, "delete_object",
5034 -1 );
5036 gtk_list_store_append( model, &iter );
5037 gtk_list_store_set( model, &iter,
5038 0, _("Cut"),
5039 1, _("Cut out from objects"),
5040 2, "difference",
5041 -1 );
5043 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5044 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5045 g_object_set_data( holder, "eraser_mode_action", act );
5047 ege_select_one_action_set_appearance( act, "full" );
5048 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5049 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5050 ege_select_one_action_set_icon_column( act, 2 );
5051 ege_select_one_action_set_tooltip_column( act, 1 );
5053 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5054 ege_select_one_action_set_active( act, eraserMode );
5055 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5056 }
5058 }
5060 //########################
5061 //## Text Toolbox ##
5062 //########################
5063 /*
5064 static void
5065 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5066 {
5067 //Call back for letter sizing spinbutton
5068 }
5070 static void
5071 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5072 {
5073 //Call back for line height spinbutton
5074 }
5076 static void
5077 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5078 {
5079 //Call back for horizontal kerning spinbutton
5080 }
5082 static void
5083 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5084 {
5085 //Call back for vertical kerning spinbutton
5086 }
5088 static void
5089 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5090 {
5091 //Call back for letter rotation spinbutton
5092 }*/
5094 namespace {
5096 bool popdown_visible = false;
5097 bool popdown_hasfocus = false;
5099 void
5100 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5101 {
5102 SPStyle *query =
5103 sp_style_new (SP_ACTIVE_DOCUMENT);
5105 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5107 int result_family =
5108 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5110 int result_style =
5111 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5113 int result_numbers =
5114 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5116 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5118 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5119 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5120 // there are no texts in selection, read from prefs
5122 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5123 if (repr) {
5124 sp_style_read_from_repr (query, repr);
5125 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5126 // do not reset the toolbar style from prefs if we already did it last time
5127 sp_style_unref(query);
5128 return;
5129 }
5130 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5131 } else {
5132 sp_style_unref(query);
5133 return;
5134 }
5135 } else {
5136 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5137 }
5139 if (query->text)
5140 {
5141 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5142 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5143 gtk_entry_set_text (GTK_ENTRY (entry), "");
5145 } else if (query->text->font_specification.value || query->text->font_family.value) {
5147 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5149 // Get the font that corresponds
5150 Glib::ustring familyName;
5152 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5153 if (font) {
5154 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5155 font->Unref();
5156 font = NULL;
5157 }
5159 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5161 Gtk::TreePath path;
5162 try {
5163 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5164 } catch (...) {
5165 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5166 sp_style_unref(query);
5167 return;
5168 }
5170 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5171 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5173 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5175 gtk_tree_selection_select_path (tselection, path.gobj());
5176 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5178 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5179 }
5181 //Size
5182 {
5183 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5184 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5185 g_object_set_data(tbl, "size-block", gpointer(1));
5186 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5187 g_object_set_data(tbl, "size-block", gpointer(0));
5188 g_free(str);
5189 }
5191 //Anchor
5192 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5193 {
5194 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5195 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5196 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5197 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5198 }
5199 else
5200 {
5201 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5202 {
5203 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5204 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5205 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5206 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5207 }
5208 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5209 {
5210 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5211 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5212 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5213 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5214 }
5215 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5216 {
5217 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5218 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5219 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5220 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5221 }
5222 }
5224 //Style
5225 {
5226 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5228 gboolean active = gtk_toggle_button_get_active (button);
5229 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5231 if (active != check)
5232 {
5233 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5234 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5235 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5236 }
5237 }
5239 {
5240 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5242 gboolean active = gtk_toggle_button_get_active (button);
5243 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5245 if (active != check)
5246 {
5247 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5248 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5249 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5250 }
5251 }
5253 //Orientation
5254 //locking both buttons, changing one affect all group (both)
5255 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5256 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5258 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5259 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5261 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5262 {
5263 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5264 }
5265 else
5266 {
5267 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5268 }
5269 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5270 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5271 }
5273 sp_style_unref(query);
5274 }
5276 void
5277 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5278 {
5279 sp_text_toolbox_selection_changed (selection, tbl);
5280 }
5282 void
5283 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5284 {
5285 sp_text_toolbox_selection_changed (NULL, tbl);
5286 }
5288 void
5289 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5290 GObject *tbl)
5291 {
5292 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5293 GtkTreeModel *model = 0;
5294 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5295 GtkTreeIter iter;
5296 char *family = 0;
5298 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5299 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5301 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5302 return;
5303 }
5305 gtk_tree_model_get (model, &iter, 0, &family, -1);
5307 if (g_object_get_data (G_OBJECT (selection), "block"))
5308 {
5309 gtk_entry_set_text (GTK_ENTRY (entry), family);
5310 return;
5311 }
5313 gtk_entry_set_text (GTK_ENTRY (entry), family);
5315 SPStyle *query =
5316 sp_style_new (SP_ACTIVE_DOCUMENT);
5318 int result_fontspec =
5319 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5321 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5323 SPCSSAttr *css = sp_repr_css_attr_new ();
5326 // First try to get the font spec from the stored value
5327 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5329 if (fontSpec.empty()) {
5330 // Construct a new font specification if it does not yet exist
5331 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5332 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5333 fontFromStyle->Unref();
5334 }
5336 if (!fontSpec.empty()) {
5337 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5338 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5339 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5340 if (font) {
5341 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5343 // Set all the these just in case they were altered when finding the best
5344 // match for the new family and old style...
5346 gchar c[256];
5348 font->Family(c, 256);
5349 sp_repr_css_set_property (css, "font-family", c);
5351 font->Attribute( "weight", c, 256);
5352 sp_repr_css_set_property (css, "font-weight", c);
5354 font->Attribute("style", c, 256);
5355 sp_repr_css_set_property (css, "font-style", c);
5357 font->Attribute("stretch", c, 256);
5358 sp_repr_css_set_property (css, "font-stretch", c);
5360 font->Attribute("variant", c, 256);
5361 sp_repr_css_set_property (css, "font-variant", c);
5363 font->Unref();
5364 }
5365 }
5366 }
5368 // If querying returned nothing, set the default style of the tool (for new texts)
5369 if (result_fontspec == QUERY_STYLE_NOTHING)
5370 {
5371 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5372 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5373 }
5374 else
5375 {
5376 sp_desktop_set_style (desktop, css, true, true);
5377 }
5379 sp_style_unref(query);
5381 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5382 _("Text: Change font family"));
5383 sp_repr_css_attr_unref (css);
5384 g_free(family);
5385 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5387 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5388 }
5390 /* This is where execution comes when the contents of the font family box have been completed
5391 by the press of the return key */
5392 void
5393 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5394 GObject *tbl)
5395 {
5396 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5398 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5399 try {
5400 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5401 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5402 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5403 gtk_tree_selection_select_path (selection, path.gobj());
5404 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5405 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5406 } catch (...) {
5407 if (family && strlen (family))
5408 {
5409 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5410 }
5411 }
5412 }
5414 void
5415 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5416 gpointer data)
5417 {
5418 if (g_object_get_data (G_OBJECT (button), "block")) return;
5419 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5420 int prop = GPOINTER_TO_INT(data);
5422 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5423 SPCSSAttr *css = sp_repr_css_attr_new ();
5425 switch (prop)
5426 {
5427 case 0:
5428 {
5429 sp_repr_css_set_property (css, "text-anchor", "start");
5430 sp_repr_css_set_property (css, "text-align", "start");
5431 break;
5432 }
5433 case 1:
5434 {
5435 sp_repr_css_set_property (css, "text-anchor", "middle");
5436 sp_repr_css_set_property (css, "text-align", "center");
5437 break;
5438 }
5440 case 2:
5441 {
5442 sp_repr_css_set_property (css, "text-anchor", "end");
5443 sp_repr_css_set_property (css, "text-align", "end");
5444 break;
5445 }
5447 case 3:
5448 {
5449 sp_repr_css_set_property (css, "text-anchor", "start");
5450 sp_repr_css_set_property (css, "text-align", "justify");
5451 break;
5452 }
5453 }
5455 SPStyle *query =
5456 sp_style_new (SP_ACTIVE_DOCUMENT);
5457 int result_numbers =
5458 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5460 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5461 if (result_numbers == QUERY_STYLE_NOTHING)
5462 {
5463 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5464 }
5466 sp_style_unref(query);
5468 sp_desktop_set_style (desktop, css, true, true);
5469 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5470 _("Text: Change alignment"));
5471 sp_repr_css_attr_unref (css);
5473 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5474 }
5476 void
5477 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5478 gpointer data)
5479 {
5480 if (g_object_get_data (G_OBJECT (button), "block")) return;
5482 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5483 SPCSSAttr *css = sp_repr_css_attr_new ();
5484 int prop = GPOINTER_TO_INT(data);
5485 bool active = gtk_toggle_button_get_active (button);
5487 SPStyle *query =
5488 sp_style_new (SP_ACTIVE_DOCUMENT);
5490 int result_fontspec =
5491 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5493 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5494 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5495 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5497 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5498 Glib::ustring newFontSpec = "";
5500 if (fontSpec.empty()) {
5501 // Construct a new font specification if it does not yet exist
5502 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5503 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5504 fontFromStyle->Unref();
5505 }
5507 switch (prop)
5508 {
5509 case 0:
5510 {
5511 if (!fontSpec.empty()) {
5512 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5513 }
5514 if (fontSpec != newFontSpec) {
5515 // Don't even set the bold if the font didn't exist on the system
5516 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5517 }
5518 break;
5519 }
5521 case 1:
5522 {
5523 if (!fontSpec.empty()) {
5524 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5525 }
5526 if (fontSpec != newFontSpec) {
5527 // Don't even set the italic if the font didn't exist on the system
5528 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5529 }
5530 break;
5531 }
5532 }
5534 if (!newFontSpec.empty()) {
5535 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5536 }
5538 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5539 if (result_fontspec == QUERY_STYLE_NOTHING)
5540 {
5541 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5542 }
5544 sp_style_unref(query);
5546 sp_desktop_set_style (desktop, css, true, true);
5547 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5548 _("Text: Change font style"));
5549 sp_repr_css_attr_unref (css);
5551 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5552 }
5554 void
5555 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5556 gpointer data)
5557 {
5558 if (g_object_get_data (G_OBJECT (button), "block")) {
5559 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5560 return;
5561 }
5563 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5564 SPCSSAttr *css = sp_repr_css_attr_new ();
5565 int prop = GPOINTER_TO_INT(data);
5567 switch (prop)
5568 {
5569 case 0:
5570 {
5571 sp_repr_css_set_property (css, "writing-mode", "lr");
5572 break;
5573 }
5575 case 1:
5576 {
5577 sp_repr_css_set_property (css, "writing-mode", "tb");
5578 break;
5579 }
5580 }
5582 SPStyle *query =
5583 sp_style_new (SP_ACTIVE_DOCUMENT);
5584 int result_numbers =
5585 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5587 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5588 if (result_numbers == QUERY_STYLE_NOTHING)
5589 {
5590 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5591 }
5593 sp_desktop_set_style (desktop, css, true, true);
5594 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5595 _("Text: Change orientation"));
5596 sp_repr_css_attr_unref (css);
5598 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5599 }
5601 gboolean
5602 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5603 {
5604 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5605 if (!desktop) return FALSE;
5607 switch (get_group0_keyval (event)) {
5608 case GDK_Escape: // defocus
5609 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5610 sp_text_toolbox_selection_changed (NULL, tbl); // update
5611 return TRUE; // I consumed the event
5612 break;
5613 }
5614 return FALSE;
5615 }
5617 gboolean
5618 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5619 {
5620 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5621 if (!desktop) return FALSE;
5623 switch (get_group0_keyval (event)) {
5624 case GDK_KP_Enter:
5625 case GDK_Return:
5626 case GDK_Escape: // defocus
5627 gtk_widget_hide (w);
5628 popdown_visible = false;
5629 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5630 return TRUE; // I consumed the event
5631 break;
5632 case GDK_w:
5633 case GDK_W:
5634 if (event->state & GDK_CONTROL_MASK) {
5635 gtk_widget_hide (w);
5636 popdown_visible = false;
5637 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5638 return TRUE; // I consumed the event
5639 }
5640 break;
5641 }
5642 return FALSE;
5643 }
5646 void
5647 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5648 GObject *tbl)
5649 {
5650 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5652 if (g_object_get_data (tbl, "size-block")) return;
5654 // If this is not from selecting a size in the list (in which case get_active will give the
5655 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5656 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5657 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5658 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5659 return;
5661 gdouble value = -1;
5662 {
5663 gchar *endptr;
5664 gchar *const text = gtk_combo_box_get_active_text(cbox);
5665 if (text) {
5666 value = g_strtod(text, &endptr);
5667 if (endptr == text) { // Conversion failed, non-numeric input.
5668 value = -1;
5669 }
5670 g_free(text);
5671 }
5672 }
5673 if (value <= 0) {
5674 return; // could not parse value
5675 }
5677 SPCSSAttr *css = sp_repr_css_attr_new ();
5678 Inkscape::CSSOStringStream osfs;
5679 osfs << value;
5680 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5682 SPStyle *query =
5683 sp_style_new (SP_ACTIVE_DOCUMENT);
5684 int result_numbers =
5685 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5687 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5688 if (result_numbers == QUERY_STYLE_NOTHING)
5689 {
5690 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5691 }
5693 sp_style_unref(query);
5695 sp_desktop_set_style (desktop, css, true, true);
5696 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5697 _("Text: Change font size"));
5698 sp_repr_css_attr_unref (css);
5700 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5701 }
5703 gboolean
5704 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5705 {
5706 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5707 if (!desktop) return FALSE;
5709 if (!g_object_get_data (tbl, "esc-pressed")) {
5710 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5711 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5712 sp_text_toolbox_size_changed (cbox, tbl);
5713 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5714 }
5715 return FALSE; // I consumed the event
5716 }
5719 gboolean
5720 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5721 {
5722 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5723 if (!desktop) return FALSE;
5725 switch (get_group0_keyval (event)) {
5726 case GDK_Escape: // defocus
5727 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5728 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5729 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5730 return TRUE; // I consumed the event
5731 break;
5732 case GDK_Return: // defocus
5733 case GDK_KP_Enter:
5734 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5735 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5736 sp_text_toolbox_size_changed (cbox, tbl);
5737 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5738 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5739 return TRUE; // I consumed the event
5740 break;
5741 }
5742 return FALSE;
5743 }
5745 void
5746 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5747 GObject *tbl)
5748 {
5749 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5750 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5751 int x, y;
5753 if (!popdown_visible)
5754 {
5755 gdk_window_get_origin (widget->window, &x, &y);
5756 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5757 gtk_widget_show_all (popdown);
5758 //sp_transientize (popdown);
5760 gdk_pointer_grab (widget->window, TRUE,
5761 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5762 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5763 GDK_POINTER_MOTION_MASK),
5764 NULL, NULL, GDK_CURRENT_TIME);
5766 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5768 popdown_visible = true;
5769 }
5770 else
5771 {
5772 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5773 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5774 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5775 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5776 gtk_widget_hide (popdown);
5777 popdown_visible = false;
5778 }
5779 }
5781 gboolean
5782 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5783 GdkEventFocus */*event*/,
5784 GObject */*tbl*/)
5785 {
5786 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5787 return FALSE;
5788 }
5790 gboolean
5791 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5792 GdkEventFocus */*event*/,
5793 GObject */*tbl*/)
5794 {
5795 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5797 if (popdown_hasfocus) {
5798 gtk_widget_hide (popdown);
5799 popdown_hasfocus = false;
5800 popdown_visible = false;
5801 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5802 return TRUE;
5803 }
5804 return FALSE;
5805 }
5807 gboolean
5808 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5809 GdkEventFocus */*event*/,
5810 GObject */*tbl*/)
5811 {
5812 popdown_hasfocus = true;
5813 return TRUE;
5814 }
5817 void
5818 cell_data_func (GtkTreeViewColumn */*column*/,
5819 GtkCellRenderer *cell,
5820 GtkTreeModel *tree_model,
5821 GtkTreeIter *iter,
5822 gpointer /*data*/)
5823 {
5824 gchar *family;
5825 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5826 gchar *const family_escaped = g_markup_escape_text(family, -1);
5828 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5829 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5831 std::stringstream markup;
5832 markup << family_escaped << " <span foreground='darkgray' font_family='"
5833 << family_escaped << "'>" << sample_escaped << "</span>";
5834 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5836 g_free(family);
5837 g_free(family_escaped);
5838 g_free(sample_escaped);
5839 }
5841 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5842 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5843 if (completion) {
5844 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5845 g_object_unref (completion);
5846 }
5847 }
5849 GtkWidget*
5850 sp_text_toolbox_new (SPDesktop *desktop)
5851 {
5852 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5853 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5855 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5856 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5858 GtkTooltips *tt = gtk_tooltips_new();
5859 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5861 ////////////Family
5862 //Window
5863 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5864 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5866 //Entry
5867 GtkWidget *entry = gtk_entry_new ();
5868 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5869 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5870 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5871 gtk_entry_completion_set_text_column (completion, 0);
5872 gtk_entry_completion_set_minimum_key_length (completion, 1);
5873 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5874 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5875 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5876 gtk_toolbar_append_widget( tbl, entry, "", "" );
5877 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5879 //Button
5880 GtkWidget *button = gtk_button_new ();
5881 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5882 gtk_toolbar_append_widget( tbl, button, "", "");
5884 //Popdown
5885 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5886 GtkWidget *treeview = gtk_tree_view_new ();
5888 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5889 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5890 gtk_tree_view_column_pack_start (column, cell, FALSE);
5891 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5892 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5893 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5895 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5896 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5897 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5899 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5901 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5902 gtk_container_add (GTK_CONTAINER (sw), treeview);
5904 gtk_container_add (GTK_CONTAINER (window), sw);
5905 gtk_widget_set_size_request (window, 300, 450);
5907 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5908 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5909 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5911 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5913 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5914 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5915 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5917 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5918 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5920 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5921 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5922 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5923 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5924 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5926 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5927 GtkWidget *box = gtk_event_box_new ();
5928 gtk_container_add (GTK_CONTAINER (box), image);
5929 gtk_toolbar_append_widget( tbl, box, "", "");
5930 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5931 GtkTooltips *tooltips = gtk_tooltips_new ();
5932 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5933 gtk_widget_hide (GTK_WIDGET (box));
5934 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5936 ////////////Size
5937 gchar const *const sizes[] = {
5938 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5939 "16", "18", "20", "22", "24", "28",
5940 "32", "36", "40", "48", "56", "64", "72", "144"
5941 };
5943 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5944 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5945 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5946 }
5947 gtk_widget_set_size_request (cbox, 80, -1);
5948 gtk_toolbar_append_widget( tbl, cbox, "", "");
5949 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5950 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5951 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5952 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5954 ////////////Text anchor
5955 GtkWidget *group = gtk_radio_button_new (NULL);
5956 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5957 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5959 // left
5960 GtkWidget *rbutton = group;
5961 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5962 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5963 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5965 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5966 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5967 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5968 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5970 // center
5971 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5972 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5973 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5974 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5976 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5977 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5978 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5979 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5981 // right
5982 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5983 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5984 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5985 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5987 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5988 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5989 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5990 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5992 // fill
5993 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5994 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5995 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5996 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5998 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5999 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6000 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6001 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6003 gtk_toolbar_append_widget( tbl, row, "", "");
6005 //spacer
6006 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6008 ////////////Text style
6009 row = gtk_hbox_new (FALSE, 4);
6011 // bold
6012 rbutton = gtk_toggle_button_new ();
6013 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6014 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6015 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6016 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6018 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6019 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6020 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6022 // italic
6023 rbutton = gtk_toggle_button_new ();
6024 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6025 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6026 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6027 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6029 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6030 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6031 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6033 gtk_toolbar_append_widget( tbl, row, "", "");
6035 //spacer
6036 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6038 ////////////Text orientation
6039 group = gtk_radio_button_new (NULL);
6040 row = gtk_hbox_new (FALSE, 4);
6041 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6043 // horizontal
6044 rbutton = group;
6045 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6046 gtk_container_add (GTK_CONTAINER (rbutton),
6047 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6048 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6049 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6051 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6052 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6053 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6055 // vertical
6056 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6057 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6058 gtk_container_add (GTK_CONTAINER (rbutton),
6059 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6060 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6061 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6063 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6064 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6065 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6066 gtk_toolbar_append_widget( tbl, row, "", "" );
6069 //watch selection
6070 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6072 sigc::connection *c_selection_changed =
6073 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6074 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6075 pool->add_connection ("selection-changed", c_selection_changed);
6077 sigc::connection *c_selection_modified =
6078 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6079 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6080 pool->add_connection ("selection-modified", c_selection_modified);
6082 sigc::connection *c_subselection_changed =
6083 new sigc::connection (desktop->connectToolSubselectionChanged
6084 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6085 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6087 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6090 gtk_widget_show_all( GTK_WIDGET(tbl) );
6092 return GTK_WIDGET(tbl);
6093 } // end of sp_text_toolbox_new()
6095 }//<unnamed> namespace
6098 //#########################
6099 //## Connector ##
6100 //#########################
6102 static void sp_connector_path_set_avoid(void)
6103 {
6104 cc_selection_set_avoid(true);
6105 }
6108 static void sp_connector_path_set_ignore(void)
6109 {
6110 cc_selection_set_avoid(false);
6111 }
6115 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6116 {
6117 // quit if run by the _changed callbacks
6118 if (g_object_get_data( tbl, "freeze" )) {
6119 return;
6120 }
6122 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6123 SPDocument *doc = sp_desktop_document(desktop);
6125 if (!sp_document_get_undo_sensitive(doc))
6126 {
6127 return;
6128 }
6130 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6132 if ( repr->attribute("inkscape:connector-spacing") ) {
6133 gdouble priorValue = gtk_adjustment_get_value(adj);
6134 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6135 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6136 return;
6137 }
6138 } else if ( adj->value == defaultConnSpacing ) {
6139 return;
6140 }
6142 // in turn, prevent callbacks from responding
6143 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6145 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6146 SP_OBJECT(desktop->namedview)->updateRepr();
6148 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6149 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6150 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6151 NR::Matrix m = NR::identity();
6152 avoid_item_move(&m, item);
6153 }
6155 if (items) {
6156 g_slist_free(items);
6157 }
6159 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6160 _("Change connector spacing"));
6162 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6164 spinbutton_defocus(GTK_OBJECT(tbl));
6165 }
6167 static void sp_connector_graph_layout(void)
6168 {
6169 if (!SP_ACTIVE_DESKTOP) return;
6171 // hack for clones, see comment in align-and-distribute.cpp
6172 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6173 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6175 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6177 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6179 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6180 }
6182 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6183 {
6184 if ( gtk_toggle_action_get_active( act ) ) {
6185 prefs_set_string_attribute("tools.connector", "directedlayout",
6186 "true");
6187 } else {
6188 prefs_set_string_attribute("tools.connector", "directedlayout",
6189 "false");
6190 }
6191 }
6193 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6194 {
6195 if ( gtk_toggle_action_get_active( act ) ) {
6196 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6197 "true");
6198 } else {
6199 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6200 "false");
6201 }
6202 }
6205 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6206 {
6207 prefs_set_double_attribute("tools.connector", "length", adj->value);
6208 spinbutton_defocus(GTK_OBJECT(tbl));
6209 }
6211 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6212 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6213 bool /*is_interactive*/, gpointer data)
6214 {
6215 GtkWidget *tbl = GTK_WIDGET(data);
6217 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6218 return;
6219 }
6220 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6221 return;
6222 }
6224 GtkAdjustment *adj = (GtkAdjustment*)
6225 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6226 gdouble spacing = defaultConnSpacing;
6227 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6229 gtk_adjustment_set_value(adj, spacing);
6230 }
6233 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6234 NULL, /* child_added */
6235 NULL, /* child_removed */
6236 connector_tb_event_attr_changed,
6237 NULL, /* content_changed */
6238 NULL /* order_changed */
6239 };
6242 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6243 {
6244 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6246 {
6247 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6248 _("Avoid"),
6249 _("Make connectors avoid selected objects"),
6250 "connector_avoid",
6251 secondarySize );
6252 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6253 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6254 }
6256 {
6257 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6258 _("Ignore"),
6259 _("Make connectors ignore selected objects"),
6260 "connector_ignore",
6261 secondarySize );
6262 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6263 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6264 }
6266 EgeAdjustmentAction* eact = 0;
6268 // Spacing spinbox
6269 eact = create_adjustment_action( "ConnectorSpacingAction",
6270 _("Connector Spacing"), _("Spacing:"),
6271 _("The amount of space left around objects by auto-routing connectors"),
6272 "tools.connector", "spacing", defaultConnSpacing,
6273 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6274 0, 100, 1.0, 10.0,
6275 0, 0, 0,
6276 connector_spacing_changed, 1, 0 );
6277 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6279 // Graph (connector network) layout
6280 {
6281 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6282 _("Graph"),
6283 _("Nicely arrange selected connector network"),
6284 "graph_layout",
6285 secondarySize );
6286 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6287 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6288 }
6290 // Default connector length spinbox
6291 eact = create_adjustment_action( "ConnectorLengthAction",
6292 _("Connector Length"), _("Length:"),
6293 _("Ideal length for connectors when layout is applied"),
6294 "tools.connector", "length", 100,
6295 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6296 10, 1000, 10.0, 100.0,
6297 0, 0, 0,
6298 connector_length_changed, 1, 0 );
6299 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6302 // Directed edges toggle button
6303 {
6304 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6305 _("Downwards"),
6306 _("Make connectors with end-markers (arrows) point downwards"),
6307 "directed_graph",
6308 Inkscape::ICON_SIZE_DECORATION );
6309 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6311 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6312 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6313 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6315 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6316 }
6318 // Avoid overlaps toggle button
6319 {
6320 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6321 _("Remove overlaps"),
6322 _("Do not allow overlapping shapes"),
6323 "remove_overlaps",
6324 Inkscape::ICON_SIZE_DECORATION );
6325 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6327 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6328 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6329 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6331 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6332 }
6334 // Code to watch for changes to the connector-spacing attribute in
6335 // the XML.
6336 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6337 g_assert(repr != NULL);
6339 purge_repr_listener( holder, holder );
6341 if (repr) {
6342 g_object_set_data( holder, "repr", repr );
6343 Inkscape::GC::anchor(repr);
6344 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6345 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6346 }
6347 } // end of sp_connector_toolbox_prep()
6350 //#########################
6351 //## Paintbucket ##
6352 //#########################
6354 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6355 {
6356 gint channels = ege_select_one_action_get_active( act );
6357 flood_channels_set_channels( channels );
6358 }
6360 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6361 {
6362 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6363 }
6365 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6366 {
6367 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6368 }
6370 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6371 {
6372 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6373 SPUnit const *unit = tracker->getActiveUnit();
6375 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6377 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6378 }
6380 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6381 {
6382 // FIXME: make defaults settable via Inkscape Options
6383 struct KeyValue {
6384 char const *key;
6385 double value;
6386 } const key_values[] = {
6387 {"threshold", 15},
6388 {"offset", 0.0}
6389 };
6391 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6392 KeyValue const &kv = key_values[i];
6393 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6394 if ( adj ) {
6395 gtk_adjustment_set_value(adj, kv.value);
6396 }
6397 }
6399 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6400 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6401 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6402 ege_select_one_action_set_active( autogap_action, 0 );
6403 }
6405 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6406 {
6407 EgeAdjustmentAction* eact = 0;
6409 {
6410 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6412 GList* items = 0;
6413 gint count = 0;
6414 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6415 {
6416 GtkTreeIter iter;
6417 gtk_list_store_append( model, &iter );
6418 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6419 count++;
6420 }
6421 g_list_free( items );
6422 items = 0;
6423 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6424 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6425 ege_select_one_action_set_appearance( act1, "compact" );
6426 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6427 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6428 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6429 g_object_set_data( holder, "channels_action", act1 );
6430 }
6432 // Spacing spinbox
6433 {
6434 eact = create_adjustment_action(
6435 "ThresholdAction",
6436 _("Fill Threshold"), _("Threshold:"),
6437 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6438 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6439 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6440 0, 0, 0,
6441 paintbucket_threshold_changed, 1, 0 );
6443 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6444 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6445 }
6447 // Create the units menu.
6448 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6449 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6450 if (stored_unit)
6451 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6452 g_object_set_data( holder, "tracker", tracker );
6453 {
6454 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6455 gtk_action_group_add_action( mainActions, act );
6456 }
6458 // Offset spinbox
6459 {
6460 eact = create_adjustment_action(
6461 "OffsetAction",
6462 _("Grow/shrink by"), _("Grow/shrink by:"),
6463 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6464 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6465 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6466 0, 0, 0,
6467 paintbucket_offset_changed, 1, 2);
6468 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6470 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6471 }
6473 /* Auto Gap */
6474 {
6475 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6477 GList* items = 0;
6478 gint count = 0;
6479 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6480 {
6481 GtkTreeIter iter;
6482 gtk_list_store_append( model, &iter );
6483 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6484 count++;
6485 }
6486 g_list_free( items );
6487 items = 0;
6488 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6489 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6490 ege_select_one_action_set_appearance( act2, "compact" );
6491 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6492 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6493 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6494 g_object_set_data( holder, "autogap_action", act2 );
6495 }
6497 /* Reset */
6498 {
6499 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6500 _("Defaults"),
6501 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6502 GTK_STOCK_CLEAR );
6503 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6504 gtk_action_group_add_action( mainActions, act );
6505 gtk_action_set_sensitive( act, TRUE );
6506 }
6508 }
6510 /*
6511 Local Variables:
6512 mode:c++
6513 c-file-style:"stroustrup"
6514 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6515 indent-tabs-mode:nil
6516 fill-column:99
6517 End:
6518 */
6519 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :