aaa5dfa863e08df11061aee62c9977f3b69e189a
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 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
172 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_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 " </toolbar>"
448 " <toolbar name='DropperToolbar'>"
449 " <toolitem action='DropperOpacityAction' />"
450 " <toolitem action='DropperPickAlphaAction' />"
451 " <toolitem action='DropperSetAlphaAction' />"
452 " </toolbar>"
454 " <toolbar name='ConnectorToolbar'>"
455 " <toolitem action='ConnectorAvoidAction' />"
456 " <toolitem action='ConnectorIgnoreAction' />"
457 " <toolitem action='ConnectorSpacingAction' />"
458 " <toolitem action='ConnectorGraphAction' />"
459 " <toolitem action='ConnectorLengthAction' />"
460 " <toolitem action='ConnectorDirectedAction' />"
461 " <toolitem action='ConnectorOverlapAction' />"
462 " </toolbar>"
464 "</ui>"
465 ;
467 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
469 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
471 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
472 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
474 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
475 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
477 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
478 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
481 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
482 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
483 Inkscape::UI::View::View *view, GtkTooltips *tt);
485 class VerbAction : public Gtk::Action {
486 public:
487 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
489 virtual ~VerbAction();
490 virtual void set_active(bool active = true);
492 protected:
493 virtual Gtk::Widget* create_menu_item_vfunc();
494 virtual Gtk::Widget* create_tool_item_vfunc();
496 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
497 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
499 virtual void on_activate();
501 private:
502 Inkscape::Verb* verb;
503 Inkscape::Verb* verb2;
504 Inkscape::UI::View::View *view;
505 GtkTooltips *tooltips;
506 bool active;
508 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
509 };
512 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
513 {
514 Glib::RefPtr<VerbAction> result;
515 SPAction *action = verb->get_action(view);
516 if ( action ) {
517 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
518 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
519 }
521 return result;
522 }
524 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
525 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
526 verb(verb),
527 verb2(verb2),
528 view(view),
529 tooltips(tooltips),
530 active(false)
531 {
532 }
534 VerbAction::~VerbAction()
535 {
536 }
538 Gtk::Widget* VerbAction::create_menu_item_vfunc()
539 {
540 // First call in to get the icon rendered if present in SVG
541 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
542 delete widget;
543 widget = 0;
545 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
546 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
547 return widg;
548 }
550 Gtk::Widget* VerbAction::create_tool_item_vfunc()
551 {
552 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
553 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
554 GtkWidget* toolbox = 0;
555 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
556 SP_BUTTON_TYPE_TOGGLE,
557 verb,
558 verb2,
559 view,
560 tooltips );
561 if ( active ) {
562 sp_button_toggle_set_down( SP_BUTTON(button), active);
563 }
564 gtk_widget_show_all( button );
565 Gtk::Widget* wrapped = Glib::wrap(button);
566 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
567 holder->add(*wrapped);
569 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
570 return holder;
571 }
573 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
574 {
575 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
576 Gtk::Action::connect_proxy_vfunc(proxy);
577 }
579 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
580 {
581 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
582 Gtk::Action::disconnect_proxy_vfunc(proxy);
583 }
585 void VerbAction::set_active(bool active)
586 {
587 this->active = active;
588 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
589 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
590 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
591 if (ti) {
592 // *should* have one child that is the SPButton
593 Gtk::Widget* child = ti->get_child();
594 if ( child && SP_IS_BUTTON(child->gobj()) ) {
595 SPButton* button = SP_BUTTON(child->gobj());
596 sp_button_toggle_set_down( button, active );
597 }
598 }
599 }
600 }
602 void VerbAction::on_activate()
603 {
604 if ( verb ) {
605 SPAction *action = verb->get_action(view);
606 if ( action ) {
607 sp_action_perform(action, 0);
608 }
609 }
610 }
612 /* Global text entry widgets necessary for update */
613 /* GtkWidget *dropper_rgb_entry,
614 *dropper_opacity_entry ; */
615 // should be made a private member once this is converted to class
617 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
618 connection->disconnect();
619 delete connection;
620 }
622 static void purge_repr_listener( GObject* obj, GObject* tbl )
623 {
624 (void)obj;
625 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
626 if (oldrepr) { // remove old listener
627 sp_repr_remove_listener_by_data(oldrepr, tbl);
628 Inkscape::GC::release(oldrepr);
629 oldrepr = 0;
630 g_object_set_data( tbl, "repr", NULL );
631 }
632 }
634 GtkWidget *
635 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
636 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
637 Inkscape::UI::View::View *view, GtkTooltips *tt)
638 {
639 SPAction *action = verb->get_action(view);
640 if (!action) return NULL;
642 SPAction *doubleclick_action;
643 if (doubleclick_verb)
644 doubleclick_action = doubleclick_verb->get_action(view);
645 else
646 doubleclick_action = NULL;
648 /* fixme: Handle sensitive/unsensitive */
649 /* fixme: Implement sp_button_new_from_action */
650 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
651 gtk_widget_show(b);
654 unsigned int shortcut = sp_shortcut_get_primary(verb);
655 if (shortcut) {
656 gchar key[256];
657 sp_ui_shortcut_string(shortcut, key);
658 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
659 if ( t ) {
660 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
661 }
662 g_free(tip);
663 } else {
664 if ( t ) {
665 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
666 }
667 }
669 return b;
670 }
673 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
674 {
675 SPAction* targetAction = SP_ACTION(user_data);
676 if ( targetAction ) {
677 sp_action_perform( targetAction, NULL );
678 }
679 }
681 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
682 {
683 if ( data ) {
684 GtkAction* act = GTK_ACTION(data);
685 gtk_action_set_sensitive( act, sensitive );
686 }
687 }
689 static SPActionEventVector action_event_vector = {
690 {NULL},
691 NULL,
692 NULL,
693 sp_action_action_set_sensitive,
694 NULL,
695 NULL
696 };
698 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
699 {
700 GtkAction* act = 0;
702 SPAction* targetAction = verb->get_action(view);
703 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
704 act = GTK_ACTION(inky);
705 gtk_action_set_sensitive( act, targetAction->sensitive );
707 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
709 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
710 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
712 return act;
713 }
715 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
716 {
717 Inkscape::UI::View::View *view = desktop;
718 gint verbsToUse[] = {
719 // disabled until we have icons for them:
720 //find
721 //SP_VERB_EDIT_TILE,
722 //SP_VERB_EDIT_UNTILE,
723 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
724 SP_VERB_DIALOG_DISPLAY,
725 SP_VERB_DIALOG_FILL_STROKE,
726 SP_VERB_DIALOG_NAMEDVIEW,
727 SP_VERB_DIALOG_TEXT,
728 SP_VERB_DIALOG_XML_EDITOR,
729 SP_VERB_EDIT_CLONE,
730 SP_VERB_EDIT_COPY,
731 SP_VERB_EDIT_CUT,
732 SP_VERB_EDIT_DUPLICATE,
733 SP_VERB_EDIT_PASTE,
734 SP_VERB_EDIT_REDO,
735 SP_VERB_EDIT_UNDO,
736 SP_VERB_EDIT_UNLINK_CLONE,
737 SP_VERB_FILE_EXPORT,
738 SP_VERB_FILE_IMPORT,
739 SP_VERB_FILE_NEW,
740 SP_VERB_FILE_OPEN,
741 SP_VERB_FILE_PRINT,
742 SP_VERB_FILE_SAVE,
743 SP_VERB_OBJECT_TO_CURVE,
744 SP_VERB_SELECTION_GROUP,
745 SP_VERB_SELECTION_OUTLINE,
746 SP_VERB_SELECTION_UNGROUP,
747 SP_VERB_ZOOM_1_1,
748 SP_VERB_ZOOM_1_2,
749 SP_VERB_ZOOM_2_1,
750 SP_VERB_ZOOM_DRAWING,
751 SP_VERB_ZOOM_IN,
752 SP_VERB_ZOOM_NEXT,
753 SP_VERB_ZOOM_OUT,
754 SP_VERB_ZOOM_PAGE,
755 SP_VERB_ZOOM_PAGE_WIDTH,
756 SP_VERB_ZOOM_PREV,
757 SP_VERB_ZOOM_SELECTION,
758 };
760 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
762 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
763 Glib::RefPtr<Gtk::ActionGroup> mainActions;
764 if ( groups.find(desktop) != groups.end() ) {
765 mainActions = groups[desktop];
766 }
768 if ( !mainActions ) {
769 mainActions = Gtk::ActionGroup::create("main");
770 groups[desktop] = mainActions;
771 }
773 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
774 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
775 if ( verb ) {
776 if (!mainActions->get_action(verb->get_id())) {
777 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
778 mainActions->add(Glib::wrap(act));
779 }
780 }
781 }
783 if ( !mainActions->get_action("ToolZoom") ) {
784 GtkTooltips *tt = gtk_tooltips_new();
785 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
786 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
787 if ( va ) {
788 mainActions->add(va);
789 if ( i == 0 ) {
790 va->set_active(true);
791 }
792 }
793 }
794 }
797 return mainActions;
798 }
801 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
802 {
803 gtk_widget_set_size_request( widget,
804 widget->allocation.width,
805 widget->allocation.height );
806 }
808 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
809 {
810 gtk_widget_set_size_request( widget, -1, -1 );
811 }
815 GtkWidget *
816 sp_tool_toolbox_new()
817 {
818 GtkTooltips *tt = gtk_tooltips_new();
819 GtkWidget* tb = gtk_toolbar_new();
820 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
821 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
823 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
824 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
826 gtk_widget_set_sensitive(tb, FALSE);
828 GtkWidget *hb = gtk_handle_box_new();
829 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
830 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
831 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
833 gtk_container_add(GTK_CONTAINER(hb), tb);
834 gtk_widget_show(GTK_WIDGET(tb));
836 sigc::connection* conn = new sigc::connection;
837 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
839 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
840 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
842 return hb;
843 }
845 GtkWidget *
846 sp_aux_toolbox_new()
847 {
848 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
850 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
852 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
854 gtk_widget_set_sensitive(tb, FALSE);
856 GtkWidget *hb = gtk_handle_box_new();
857 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
858 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
859 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
861 gtk_container_add(GTK_CONTAINER(hb), tb);
862 gtk_widget_show(GTK_WIDGET(tb));
864 sigc::connection* conn = new sigc::connection;
865 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
867 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
868 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
870 return hb;
871 }
873 //####################################
874 //# Commands Bar
875 //####################################
877 GtkWidget *
878 sp_commands_toolbox_new()
879 {
880 GtkWidget *tb = gtk_toolbar_new();
882 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
883 gtk_widget_set_sensitive(tb, FALSE);
885 GtkWidget *hb = gtk_handle_box_new();
886 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
887 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
888 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
890 gtk_container_add(GTK_CONTAINER(hb), tb);
891 gtk_widget_show(GTK_WIDGET(tb));
893 sigc::connection* conn = new sigc::connection;
894 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
896 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
897 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
899 return hb;
900 }
903 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
904 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
905 gchar const *path, gchar const *data, gdouble def,
906 GtkWidget *focusTarget,
907 GtkWidget *us,
908 GObject *dataKludge,
909 gboolean altx, gchar const *altx_mark,
910 gdouble lower, gdouble upper, gdouble step, gdouble page,
911 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
912 void (*callback)(GtkAdjustment *, GObject *),
913 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
914 {
915 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
916 lower, upper, step, page, page ) );
917 if (us) {
918 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
919 }
921 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
923 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
924 if ( shortLabel ) {
925 g_object_set( act, "short_label", shortLabel, NULL );
926 }
928 if ( (descrCount > 0) && descrLabels && descrValues ) {
929 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
930 }
932 if ( focusTarget ) {
933 ege_adjustment_action_set_focuswidget( act, focusTarget );
934 }
936 if ( altx && altx_mark ) {
937 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
938 }
940 if ( dataKludge ) {
941 g_object_set_data( dataKludge, data, adj );
942 }
944 // Using a cast just to make sure we pass in the right kind of function pointer
945 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
947 return act;
948 }
951 //####################################
952 //# node editing callbacks
953 //####################################
955 /**
956 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
957 */
958 static ShapeEditor *get_current_shape_editor()
959 {
960 if (!SP_ACTIVE_DESKTOP) {
961 return NULL;
962 }
964 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
966 if (!SP_IS_NODE_CONTEXT(event_context)) {
967 return NULL;
968 }
970 return SP_NODE_CONTEXT(event_context)->shape_editor;
971 }
974 void
975 sp_node_path_edit_add(void)
976 {
977 ShapeEditor *shape_editor = get_current_shape_editor();
978 if (shape_editor) shape_editor->add_node();
979 }
981 void
982 sp_node_path_edit_delete(void)
983 {
984 ShapeEditor *shape_editor = get_current_shape_editor();
985 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
986 }
988 void
989 sp_node_path_edit_delete_segment(void)
990 {
991 ShapeEditor *shape_editor = get_current_shape_editor();
992 if (shape_editor) shape_editor->delete_segment();
993 }
995 void
996 sp_node_path_edit_break(void)
997 {
998 ShapeEditor *shape_editor = get_current_shape_editor();
999 if (shape_editor) shape_editor->break_at_nodes();
1000 }
1002 void
1003 sp_node_path_edit_join(void)
1004 {
1005 ShapeEditor *shape_editor = get_current_shape_editor();
1006 if (shape_editor) shape_editor->join_nodes();
1007 }
1009 void
1010 sp_node_path_edit_join_segment(void)
1011 {
1012 ShapeEditor *shape_editor = get_current_shape_editor();
1013 if (shape_editor) shape_editor->join_segments();
1014 }
1016 void
1017 sp_node_path_edit_toline(void)
1018 {
1019 ShapeEditor *shape_editor = get_current_shape_editor();
1020 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1021 }
1023 void
1024 sp_node_path_edit_tocurve(void)
1025 {
1026 ShapeEditor *shape_editor = get_current_shape_editor();
1027 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1028 }
1030 void
1031 sp_node_path_edit_cusp(void)
1032 {
1033 ShapeEditor *shape_editor = get_current_shape_editor();
1034 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1035 }
1037 void
1038 sp_node_path_edit_smooth(void)
1039 {
1040 ShapeEditor *shape_editor = get_current_shape_editor();
1041 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1042 }
1044 void
1045 sp_node_path_edit_symmetrical(void)
1046 {
1047 ShapeEditor *shape_editor = get_current_shape_editor();
1048 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1049 }
1051 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1052 bool show = gtk_toggle_action_get_active( act );
1053 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1054 ShapeEditor *shape_editor = get_current_shape_editor();
1055 if (shape_editor) shape_editor->show_handles(show);
1056 }
1058 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1059 bool show = gtk_toggle_action_get_active( act );
1060 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1061 ShapeEditor *shape_editor = get_current_shape_editor();
1062 if (shape_editor) shape_editor->show_helperpath(show);
1063 }
1065 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1066 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1067 }
1069 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1070 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1071 }
1073 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1074 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1075 }
1077 /* is called when the node selection is modified */
1078 static void
1079 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1080 {
1081 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1082 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1083 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1084 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1086 // quit if run by the attr_changed listener
1087 if (g_object_get_data( tbl, "freeze" )) {
1088 return;
1089 }
1091 // in turn, prevent listener from responding
1092 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1094 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1095 SPUnit const *unit = tracker->getActiveUnit();
1097 ShapeEditor *shape_editor = get_current_shape_editor();
1098 if (shape_editor && shape_editor->has_nodepath()) {
1099 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1100 int n_selected = 0;
1101 if (nodepath) {
1102 n_selected = nodepath->numSelected();
1103 }
1105 if (n_selected == 0) {
1106 gtk_action_set_sensitive(xact, FALSE);
1107 gtk_action_set_sensitive(yact, FALSE);
1108 } else {
1109 gtk_action_set_sensitive(xact, TRUE);
1110 gtk_action_set_sensitive(yact, TRUE);
1111 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1112 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1114 if (n_selected == 1) {
1115 Geom::Point sel_node = nodepath->singleSelectedCoords();
1116 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1117 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1118 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1119 }
1120 } else {
1121 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1122 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1123 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1124 /* Note: Currently x and y will always have a value, even if the coordinates of the
1125 selected nodes don't coincide (in this case we use the coordinates of the center
1126 of the bounding box). So the entries are never set to zero. */
1127 // FIXME: Maybe we should clear the entry if several nodes are selected
1128 // instead of providing a kind of average value
1129 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1130 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1131 }
1132 }
1133 }
1134 } else {
1135 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1136 gtk_action_set_sensitive(xact, FALSE);
1137 gtk_action_set_sensitive(yact, FALSE);
1138 }
1140 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1141 }
1143 static void
1144 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1145 {
1146 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1148 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1149 SPUnit const *unit = tracker->getActiveUnit();
1151 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1152 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1153 }
1155 // quit if run by the attr_changed listener
1156 if (g_object_get_data( tbl, "freeze" )) {
1157 return;
1158 }
1160 // in turn, prevent listener from responding
1161 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1163 ShapeEditor *shape_editor = get_current_shape_editor();
1164 if (shape_editor && shape_editor->has_nodepath()) {
1165 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1166 if (!strcmp(value_name, "x")) {
1167 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1168 }
1169 if (!strcmp(value_name, "y")) {
1170 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1171 }
1172 }
1174 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1175 }
1177 static void
1178 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1179 {
1180 sp_node_path_value_changed(adj, tbl, "x");
1181 }
1183 static void
1184 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1185 {
1186 sp_node_path_value_changed(adj, tbl, "y");
1187 }
1189 void
1190 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1191 {
1192 {
1193 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1194 SPItem *item = selection->singleItem();
1195 if (item && SP_IS_LPE_ITEM(item)) {
1196 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1197 gtk_action_set_sensitive(w, TRUE);
1198 } else {
1199 gtk_action_set_sensitive(w, FALSE);
1200 }
1201 } else {
1202 gtk_action_set_sensitive(w, FALSE);
1203 }
1204 }
1206 {
1207 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1208 SPItem *item = selection->singleItem();
1209 if (item && item->clip_ref && item->clip_ref->getObject()) {
1210 gtk_action_set_sensitive(w, TRUE);
1211 } else {
1212 gtk_action_set_sensitive(w, FALSE);
1213 }
1214 }
1216 {
1217 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1218 SPItem *item = selection->singleItem();
1219 if (item && item->mask_ref && item->mask_ref->getObject()) {
1220 gtk_action_set_sensitive(w, TRUE);
1221 } else {
1222 gtk_action_set_sensitive(w, FALSE);
1223 }
1224 }
1225 }
1227 void
1228 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1229 {
1230 sp_node_toolbox_sel_changed (selection, tbl);
1231 }
1235 //################################
1236 //## Node Editing Toolbox ##
1237 //################################
1239 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1240 {
1241 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1242 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1243 g_object_set_data( holder, "tracker", tracker );
1245 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1247 {
1248 InkAction* inky = ink_action_new( "NodeInsertAction",
1249 _("Insert node"),
1250 _("Insert new nodes into selected segments"),
1251 "node_insert",
1252 secondarySize );
1253 g_object_set( inky, "short_label", _("Insert"), NULL );
1254 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1255 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1256 }
1258 {
1259 InkAction* inky = ink_action_new( "NodeDeleteAction",
1260 _("Delete node"),
1261 _("Delete selected nodes"),
1262 "node_delete",
1263 secondarySize );
1264 g_object_set( inky, "short_label", _("Delete"), NULL );
1265 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1266 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1267 }
1269 {
1270 InkAction* inky = ink_action_new( "NodeJoinAction",
1271 _("Join endnodes"),
1272 _("Join selected endnodes"),
1273 "node_join",
1274 secondarySize );
1275 g_object_set( inky, "short_label", _("Join"), NULL );
1276 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1277 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1278 }
1280 {
1281 InkAction* inky = ink_action_new( "NodeBreakAction",
1282 _("Break nodes"),
1283 _("Break path at selected nodes"),
1284 "node_break",
1285 secondarySize );
1286 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1287 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1288 }
1291 {
1292 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1293 _("Join with segment"),
1294 _("Join selected endnodes with a new segment"),
1295 "node_join_segment",
1296 secondarySize );
1297 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1298 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299 }
1301 {
1302 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1303 _("Delete segment"),
1304 _("Delete segment between two non-endpoint nodes"),
1305 "node_delete_segment",
1306 secondarySize );
1307 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1308 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1309 }
1311 {
1312 InkAction* inky = ink_action_new( "NodeCuspAction",
1313 _("Node Cusp"),
1314 _("Make selected nodes corner"),
1315 "node_cusp",
1316 secondarySize );
1317 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1318 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1319 }
1321 {
1322 InkAction* inky = ink_action_new( "NodeSmoothAction",
1323 _("Node Smooth"),
1324 _("Make selected nodes smooth"),
1325 "node_smooth",
1326 secondarySize );
1327 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1328 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1329 }
1331 {
1332 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1333 _("Node Symmetric"),
1334 _("Make selected nodes symmetric"),
1335 "node_symmetric",
1336 secondarySize );
1337 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1338 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339 }
1341 {
1342 InkAction* inky = ink_action_new( "NodeLineAction",
1343 _("Node Line"),
1344 _("Make selected segments lines"),
1345 "node_line",
1346 secondarySize );
1347 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1348 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1349 }
1351 {
1352 InkAction* inky = ink_action_new( "NodeCurveAction",
1353 _("Node Curve"),
1354 _("Make selected segments curves"),
1355 "node_curve",
1356 secondarySize );
1357 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1358 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1359 }
1361 {
1362 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1363 _("Show Handles"),
1364 _("Show the Bezier handles of selected nodes"),
1365 "nodes_show_handles",
1366 Inkscape::ICON_SIZE_DECORATION );
1367 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1368 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1369 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1370 }
1372 {
1373 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1374 _("Show Outline"),
1375 _("Show the outline of the path"),
1376 "nodes_show_helperpath",
1377 Inkscape::ICON_SIZE_DECORATION );
1378 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1379 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1380 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1381 }
1383 {
1384 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1385 _("Next path effect parameter"),
1386 _("Show next path effect parameter for editing"),
1387 "edit_next_parameter",
1388 Inkscape::ICON_SIZE_DECORATION );
1389 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1390 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1391 g_object_set_data( holder, "nodes_lpeedit", inky);
1392 }
1394 {
1395 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1396 _("Edit clipping path"),
1397 _("Edit the clipping path of the object"),
1398 "nodeedit-clippath",
1399 Inkscape::ICON_SIZE_DECORATION );
1400 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1401 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402 g_object_set_data( holder, "nodes_clippathedit", inky);
1403 }
1405 {
1406 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1407 _("Edit mask path"),
1408 _("Edit the mask of the object"),
1409 "nodeedit-mask",
1410 Inkscape::ICON_SIZE_DECORATION );
1411 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1412 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1413 g_object_set_data( holder, "nodes_maskedit", inky);
1414 }
1416 /* X coord of selected node(s) */
1417 {
1418 EgeAdjustmentAction* eact = 0;
1419 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1420 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1421 eact = create_adjustment_action( "NodeXAction",
1422 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1423 "tools.nodes", "Xcoord", 0,
1424 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1425 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1426 labels, values, G_N_ELEMENTS(labels),
1427 sp_node_path_x_value_changed );
1428 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1429 g_object_set_data( holder, "nodes_x_action", eact );
1430 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1431 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1432 }
1434 /* Y coord of selected node(s) */
1435 {
1436 EgeAdjustmentAction* eact = 0;
1437 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1438 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1439 eact = create_adjustment_action( "NodeYAction",
1440 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1441 "tools.nodes", "Ycoord", 0,
1442 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1443 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1444 labels, values, G_N_ELEMENTS(labels),
1445 sp_node_path_y_value_changed );
1446 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1447 g_object_set_data( holder, "nodes_y_action", eact );
1448 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1449 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1450 }
1452 // add the units menu
1453 {
1454 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1455 gtk_action_group_add_action( mainActions, act );
1456 }
1459 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1461 //watch selection
1462 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1464 sigc::connection *c_selection_changed =
1465 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1466 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1467 pool->add_connection ("selection-changed", c_selection_changed);
1469 sigc::connection *c_selection_modified =
1470 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1471 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1472 pool->add_connection ("selection-modified", c_selection_modified);
1474 sigc::connection *c_subselection_changed =
1475 new sigc::connection (desktop->connectToolSubselectionChanged
1476 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1477 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1479 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1481 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1482 } // end of sp_node_toolbox_prep()
1485 //########################
1486 //## Zoom Toolbox ##
1487 //########################
1489 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1490 {
1491 // no custom GtkAction setup needed
1492 } // end of sp_zoom_toolbox_prep()
1494 void
1495 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1496 {
1497 toolbox_set_desktop(toolbox,
1498 desktop,
1499 setup_tool_toolbox,
1500 update_tool_toolbox,
1501 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1502 "event_context_connection")));
1503 }
1506 void
1507 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1508 {
1509 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1510 desktop,
1511 setup_aux_toolbox,
1512 update_aux_toolbox,
1513 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1514 "event_context_connection")));
1515 }
1517 void
1518 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1519 {
1520 toolbox_set_desktop(toolbox,
1521 desktop,
1522 setup_commands_toolbox,
1523 update_commands_toolbox,
1524 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1525 "event_context_connection")));
1526 }
1528 static void
1529 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1530 {
1531 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1532 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1534 if (old_desktop) {
1535 GList *children, *iter;
1537 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1538 for ( iter = children ; iter ; iter = iter->next ) {
1539 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1540 }
1541 g_list_free(children);
1542 }
1544 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1546 if (desktop) {
1547 gtk_widget_set_sensitive(toolbox, TRUE);
1548 setup_func(toolbox, desktop);
1549 update_func(desktop, desktop->event_context, toolbox);
1550 *conn = desktop->connectEventContextChanged
1551 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1552 } else {
1553 gtk_widget_set_sensitive(toolbox, FALSE);
1554 }
1556 } // end of toolbox_set_desktop()
1559 static void
1560 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1561 {
1562 gchar const * descr =
1563 "<ui>"
1564 " <toolbar name='ToolToolbar'>"
1565 " <toolitem action='ToolSelector' />"
1566 " <toolitem action='ToolNode' />"
1567 " <toolitem action='ToolTweak' />"
1568 " <toolitem action='ToolZoom' />"
1569 " <toolitem action='ToolRect' />"
1570 " <toolitem action='Tool3DBox' />"
1571 " <toolitem action='ToolArc' />"
1572 " <toolitem action='ToolStar' />"
1573 " <toolitem action='ToolSpiral' />"
1574 " <toolitem action='ToolPencil' />"
1575 " <toolitem action='ToolPen' />"
1576 " <toolitem action='ToolCalligraphic' />"
1577 " <toolitem action='ToolEraser' />"
1578 " <toolitem action='ToolLPETool' />"
1579 " <toolitem action='ToolPaintBucket' />"
1580 " <toolitem action='ToolText' />"
1581 " <toolitem action='ToolConnector' />"
1582 " <toolitem action='ToolGradient' />"
1583 " <toolitem action='ToolDropper' />"
1584 " </toolbar>"
1585 "</ui>";
1586 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1587 GtkUIManager* mgr = gtk_ui_manager_new();
1588 GError* errVal = 0;
1590 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1591 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1593 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1594 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1595 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1596 }
1597 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1598 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1600 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1601 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1603 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1605 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1606 if ( child ) {
1607 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1608 }
1610 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1611 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1612 }
1615 static void
1616 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1617 {
1618 gchar const *const tname = ( eventcontext
1619 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1620 : NULL );
1621 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1623 for (int i = 0 ; tools[i].type_name ; i++ ) {
1624 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1625 if ( act ) {
1626 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1627 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1628 if ( verbAct ) {
1629 verbAct->set_active(setActive);
1630 }
1631 }
1632 }
1633 }
1635 static void
1636 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1637 {
1638 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1639 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1640 GtkUIManager* mgr = gtk_ui_manager_new();
1641 GError* errVal = 0;
1642 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1643 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1645 std::map<std::string, GtkWidget*> dataHolders;
1647 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1648 if ( aux_toolboxes[i].prep_func ) {
1649 // converted to GtkActions and UIManager
1651 GtkWidget* kludge = gtk_toolbar_new();
1652 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1653 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1654 dataHolders[aux_toolboxes[i].type_name] = kludge;
1655 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1656 } else {
1658 GtkWidget *sub_toolbox = 0;
1659 if (aux_toolboxes[i].create_func == NULL)
1660 sub_toolbox = sp_empty_toolbox_new(desktop);
1661 else {
1662 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1663 }
1665 gtk_size_group_add_widget( grouper, sub_toolbox );
1667 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1668 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1670 }
1671 }
1673 // Second pass to create toolbars *after* all GtkActions are created
1674 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1675 if ( aux_toolboxes[i].prep_func ) {
1676 // converted to GtkActions and UIManager
1678 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1680 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1681 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1683 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1684 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1685 g_free( tmp );
1686 tmp = 0;
1688 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1689 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1690 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1691 }
1692 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1695 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1697 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1698 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1699 swatch->setDesktop( desktop );
1700 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1701 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1702 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1703 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 );
1704 }
1706 gtk_widget_show_all( holder );
1707 sp_set_font_size_smaller( holder );
1709 gtk_size_group_add_widget( grouper, holder );
1711 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1712 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1713 }
1714 }
1716 g_object_unref( G_OBJECT(grouper) );
1717 }
1719 static void
1720 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1721 {
1722 gchar const *tname = ( eventcontext
1723 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1724 : NULL );
1725 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1726 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1727 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1728 gtk_widget_show_all(sub_toolbox);
1729 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1730 } else {
1731 gtk_widget_hide(sub_toolbox);
1732 }
1733 }
1734 }
1736 static void
1737 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1738 {
1739 gchar const * descr =
1740 "<ui>"
1741 " <toolbar name='CommandsToolbar'>"
1742 " <toolitem action='FileNew' />"
1743 " <toolitem action='FileOpen' />"
1744 " <toolitem action='FileSave' />"
1745 " <toolitem action='FilePrint' />"
1746 " <separator />"
1747 " <toolitem action='FileImport' />"
1748 " <toolitem action='FileExport' />"
1749 " <separator />"
1750 " <toolitem action='EditUndo' />"
1751 " <toolitem action='EditRedo' />"
1752 " <separator />"
1753 " <toolitem action='EditCopy' />"
1754 " <toolitem action='EditCut' />"
1755 " <toolitem action='EditPaste' />"
1756 " <separator />"
1757 " <toolitem action='ZoomSelection' />"
1758 " <toolitem action='ZoomDrawing' />"
1759 " <toolitem action='ZoomPage' />"
1760 " <separator />"
1761 " <toolitem action='EditDuplicate' />"
1762 " <toolitem action='EditClone' />"
1763 " <toolitem action='EditUnlinkClone' />"
1764 " <separator />"
1765 " <toolitem action='SelectionGroup' />"
1766 " <toolitem action='SelectionUnGroup' />"
1767 " <separator />"
1768 " <toolitem action='DialogFillStroke' />"
1769 " <toolitem action='DialogText' />"
1770 " <toolitem action='DialogXMLEditor' />"
1771 " <toolitem action='DialogAlignDistribute' />"
1772 " <separator />"
1773 " <toolitem action='DialogPreferences' />"
1774 " <toolitem action='DialogDocumentProperties' />"
1775 " </toolbar>"
1776 "</ui>";
1777 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1780 GtkUIManager* mgr = gtk_ui_manager_new();
1781 GError* errVal = 0;
1783 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1784 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1786 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1787 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1788 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1789 }
1791 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1792 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1794 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1795 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1798 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1800 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1801 if ( child ) {
1802 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1803 }
1805 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1806 }
1808 static void
1809 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1810 {
1811 }
1813 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1814 {
1815 gtk_widget_show(toolbox_toplevel);
1816 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1818 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1819 if (!shown_toolbox) {
1820 return;
1821 }
1822 gtk_widget_show(toolbox);
1824 gtk_widget_show_all(shown_toolbox);
1825 }
1827 static GtkWidget *
1828 sp_empty_toolbox_new(SPDesktop *desktop)
1829 {
1830 GtkWidget *tbl = gtk_toolbar_new();
1831 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1832 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1834 gtk_widget_show_all(tbl);
1835 sp_set_font_size_smaller (tbl);
1837 return tbl;
1838 }
1840 #define MODE_LABEL_WIDTH 70
1842 //########################
1843 //## Star ##
1844 //########################
1846 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1847 {
1848 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1850 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1851 // do not remember prefs if this call is initiated by an undo change, because undoing object
1852 // creation sets bogus values to its attributes before it is deleted
1853 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1854 }
1856 // quit if run by the attr_changed listener
1857 if (g_object_get_data( dataKludge, "freeze" )) {
1858 return;
1859 }
1861 // in turn, prevent listener from responding
1862 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1864 bool modmade = false;
1866 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1867 GSList const *items = selection->itemList();
1868 for (; items != NULL; items = items->next) {
1869 if (SP_IS_STAR((SPItem *) items->data)) {
1870 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1871 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1872 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1873 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1874 + M_PI / (gint)adj->value));
1875 SP_OBJECT((SPItem *) items->data)->updateRepr();
1876 modmade = true;
1877 }
1878 }
1879 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1880 _("Star: Change number of corners"));
1882 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1883 }
1885 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1886 {
1887 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1889 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1890 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1891 }
1893 // quit if run by the attr_changed listener
1894 if (g_object_get_data( dataKludge, "freeze" )) {
1895 return;
1896 }
1898 // in turn, prevent listener from responding
1899 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1901 bool modmade = false;
1902 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1903 GSList const *items = selection->itemList();
1904 for (; items != NULL; items = items->next) {
1905 if (SP_IS_STAR((SPItem *) items->data)) {
1906 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1908 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1909 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1910 if (r2 < r1) {
1911 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1912 } else {
1913 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1914 }
1916 SP_OBJECT((SPItem *) items->data)->updateRepr();
1917 modmade = true;
1918 }
1919 }
1921 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1922 _("Star: Change spoke ratio"));
1924 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1925 }
1927 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1928 {
1929 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1930 bool flat = ege_select_one_action_get_active( act ) == 0;
1932 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1933 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1934 flat ? "true" : "false" );
1935 }
1937 // quit if run by the attr_changed listener
1938 if (g_object_get_data( dataKludge, "freeze" )) {
1939 return;
1940 }
1942 // in turn, prevent listener from responding
1943 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1945 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1946 GSList const *items = selection->itemList();
1947 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1948 bool modmade = false;
1950 if ( prop_action ) {
1951 gtk_action_set_sensitive( prop_action, !flat );
1952 }
1954 for (; items != NULL; items = items->next) {
1955 if (SP_IS_STAR((SPItem *) items->data)) {
1956 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1957 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1958 SP_OBJECT((SPItem *) items->data)->updateRepr();
1959 modmade = true;
1960 }
1961 }
1963 if (modmade) {
1964 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1965 flat ? _("Make polygon") : _("Make star"));
1966 }
1968 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1969 }
1971 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1972 {
1973 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1975 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1976 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1977 }
1979 // quit if run by the attr_changed listener
1980 if (g_object_get_data( dataKludge, "freeze" )) {
1981 return;
1982 }
1984 // in turn, prevent listener from responding
1985 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1987 bool modmade = false;
1989 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1990 GSList const *items = selection->itemList();
1991 for (; items != NULL; items = items->next) {
1992 if (SP_IS_STAR((SPItem *) items->data)) {
1993 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1994 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1995 SP_OBJECT(items->data)->updateRepr();
1996 modmade = true;
1997 }
1998 }
1999 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2000 _("Star: Change rounding"));
2002 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2003 }
2005 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2006 {
2007 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2009 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2010 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2011 }
2013 // quit if run by the attr_changed listener
2014 if (g_object_get_data( dataKludge, "freeze" )) {
2015 return;
2016 }
2018 // in turn, prevent listener from responding
2019 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2021 bool modmade = false;
2023 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2024 GSList const *items = selection->itemList();
2025 for (; items != NULL; items = items->next) {
2026 if (SP_IS_STAR((SPItem *) items->data)) {
2027 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2028 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2029 SP_OBJECT(items->data)->updateRepr();
2030 modmade = true;
2031 }
2032 }
2033 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2034 _("Star: Change randomization"));
2036 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2037 }
2040 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2041 gchar const */*old_value*/, gchar const */*new_value*/,
2042 bool /*is_interactive*/, gpointer data)
2043 {
2044 GtkWidget *tbl = GTK_WIDGET(data);
2046 // quit if run by the _changed callbacks
2047 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2048 return;
2049 }
2051 // in turn, prevent callbacks from responding
2052 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2054 GtkAdjustment *adj = 0;
2056 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2057 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2059 if (!strcmp(name, "inkscape:randomized")) {
2060 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2061 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2062 } else if (!strcmp(name, "inkscape:rounded")) {
2063 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2064 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2065 } else if (!strcmp(name, "inkscape:flatsided")) {
2066 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2067 char const *flatsides = repr->attribute("inkscape:flatsided");
2068 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2069 if ( flatsides && !strcmp(flatsides,"false") ) {
2070 ege_select_one_action_set_active( flat_action, 1 );
2071 gtk_action_set_sensitive( prop_action, TRUE );
2072 } else {
2073 ege_select_one_action_set_active( flat_action, 0 );
2074 gtk_action_set_sensitive( prop_action, FALSE );
2075 }
2076 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2077 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2078 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2079 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2080 if (r2 < r1) {
2081 gtk_adjustment_set_value(adj, r2/r1);
2082 } else {
2083 gtk_adjustment_set_value(adj, r1/r2);
2084 }
2085 } else if (!strcmp(name, "sodipodi:sides")) {
2086 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2087 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2088 }
2090 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2091 }
2094 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2095 {
2096 NULL, /* child_added */
2097 NULL, /* child_removed */
2098 star_tb_event_attr_changed,
2099 NULL, /* content_changed */
2100 NULL /* order_changed */
2101 };
2104 /**
2105 * \param selection Should not be NULL.
2106 */
2107 static void
2108 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2109 {
2110 int n_selected = 0;
2111 Inkscape::XML::Node *repr = NULL;
2113 purge_repr_listener( tbl, tbl );
2115 for (GSList const *items = selection->itemList();
2116 items != NULL;
2117 items = items->next)
2118 {
2119 if (SP_IS_STAR((SPItem *) items->data)) {
2120 n_selected++;
2121 repr = SP_OBJECT_REPR((SPItem *) items->data);
2122 }
2123 }
2125 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2127 if (n_selected == 0) {
2128 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2129 } else if (n_selected == 1) {
2130 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2132 if (repr) {
2133 g_object_set_data( tbl, "repr", repr );
2134 Inkscape::GC::anchor(repr);
2135 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2136 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2137 }
2138 } else {
2139 // FIXME: implement averaging of all parameters for multiple selected stars
2140 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2141 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2142 }
2143 }
2146 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2147 {
2148 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2149 // callbacks to lump all the changes for all selected objects in one undo step
2151 GtkAdjustment *adj = 0;
2153 // fixme: make settable in prefs!
2154 gint mag = 5;
2155 gdouble prop = 0.5;
2156 gboolean flat = FALSE;
2157 gdouble randomized = 0;
2158 gdouble rounded = 0;
2160 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2161 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2163 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2164 gtk_action_set_sensitive( sb2, !flat );
2166 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2167 gtk_adjustment_set_value(adj, mag);
2168 gtk_adjustment_value_changed(adj);
2170 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2171 gtk_adjustment_set_value(adj, prop);
2172 gtk_adjustment_value_changed(adj);
2174 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2175 gtk_adjustment_set_value(adj, rounded);
2176 gtk_adjustment_value_changed(adj);
2178 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2179 gtk_adjustment_set_value(adj, randomized);
2180 gtk_adjustment_value_changed(adj);
2181 }
2184 void
2185 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2186 {
2187 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2188 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2189 GtkWidget *l = gtk_label_new(NULL);
2190 gtk_label_set_markup(GTK_LABEL(l), title);
2191 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2192 if ( GTK_IS_TOOLBAR(tbl) ) {
2193 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2194 } else {
2195 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2196 }
2197 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2198 }
2201 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2202 {
2203 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2205 {
2206 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2207 ege_output_action_set_use_markup( act, TRUE );
2208 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2209 g_object_set_data( holder, "mode_action", act );
2210 }
2212 {
2213 EgeAdjustmentAction* eact = 0;
2214 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2215 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2217 /* Flatsided checkbox */
2218 {
2219 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2221 GtkTreeIter iter;
2222 gtk_list_store_append( model, &iter );
2223 gtk_list_store_set( model, &iter,
2224 0, _("Polygon"),
2225 1, _("Regular polygon (with one handle) instead of a star"),
2226 2, "star_flat",
2227 -1 );
2229 gtk_list_store_append( model, &iter );
2230 gtk_list_store_set( model, &iter,
2231 0, _("Star"),
2232 1, _("Star instead of a regular polygon (with one handle)"),
2233 2, "star_angled",
2234 -1 );
2236 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2237 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2238 g_object_set_data( holder, "flat_action", act );
2240 ege_select_one_action_set_appearance( act, "full" );
2241 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2242 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2243 ege_select_one_action_set_icon_column( act, 2 );
2244 ege_select_one_action_set_icon_size( act, secondarySize );
2245 ege_select_one_action_set_tooltip_column( act, 1 );
2247 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2248 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2249 }
2251 /* Magnitude */
2252 {
2253 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2254 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2255 eact = create_adjustment_action( "MagnitudeAction",
2256 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2257 "tools.shapes.star", "magnitude", 3,
2258 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2259 3, 1024, 1, 5,
2260 labels, values, G_N_ELEMENTS(labels),
2261 sp_stb_magnitude_value_changed,
2262 1.0, 0 );
2263 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2264 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2265 }
2267 /* Spoke ratio */
2268 {
2269 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2270 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2271 eact = create_adjustment_action( "SpokeAction",
2272 _("Spoke ratio"), _("Spoke ratio:"),
2273 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2274 // Base radius is the same for the closest handle.
2275 _("Base radius to tip radius ratio"),
2276 "tools.shapes.star", "proportion", 0.5,
2277 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2278 0.01, 1.0, 0.01, 0.1,
2279 labels, values, G_N_ELEMENTS(labels),
2280 sp_stb_proportion_value_changed );
2281 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2282 g_object_set_data( holder, "prop_action", eact );
2283 }
2285 if ( !isFlatSided ) {
2286 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2287 } else {
2288 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2289 }
2291 /* Roundedness */
2292 {
2293 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2294 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2295 eact = create_adjustment_action( "RoundednessAction",
2296 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2297 "tools.shapes.star", "rounded", 0.0,
2298 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2299 -10.0, 10.0, 0.01, 0.1,
2300 labels, values, G_N_ELEMENTS(labels),
2301 sp_stb_rounded_value_changed );
2302 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2303 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2304 }
2306 /* Randomization */
2307 {
2308 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2309 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2310 eact = create_adjustment_action( "RandomizationAction",
2311 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2312 "tools.shapes.star", "randomized", 0.0,
2313 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2314 -10.0, 10.0, 0.001, 0.01,
2315 labels, values, G_N_ELEMENTS(labels),
2316 sp_stb_randomized_value_changed, 0.1, 3 );
2317 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2318 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2319 }
2320 }
2322 {
2323 /* Reset */
2324 {
2325 GtkAction* act = gtk_action_new( "StarResetAction",
2326 _("Defaults"),
2327 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2328 GTK_STOCK_CLEAR );
2329 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2330 gtk_action_group_add_action( mainActions, act );
2331 gtk_action_set_sensitive( act, TRUE );
2332 }
2333 }
2335 sigc::connection *connection = new sigc::connection(
2336 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2337 );
2338 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2339 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2340 }
2343 //########################
2344 //## Rect ##
2345 //########################
2347 static void sp_rtb_sensitivize( GObject *tbl )
2348 {
2349 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2350 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2351 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2353 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2354 gtk_action_set_sensitive( not_rounded, FALSE );
2355 } else {
2356 gtk_action_set_sensitive( not_rounded, TRUE );
2357 }
2358 }
2361 static void
2362 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2363 void (*setter)(SPRect *, gdouble))
2364 {
2365 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2367 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2368 SPUnit const *unit = tracker->getActiveUnit();
2370 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2371 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2372 }
2374 // quit if run by the attr_changed listener
2375 if (g_object_get_data( tbl, "freeze" )) {
2376 return;
2377 }
2379 // in turn, prevent listener from responding
2380 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2382 bool modmade = false;
2383 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2384 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2385 if (SP_IS_RECT(items->data)) {
2386 if (adj->value != 0) {
2387 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2388 } else {
2389 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2390 }
2391 modmade = true;
2392 }
2393 }
2395 sp_rtb_sensitivize( tbl );
2397 if (modmade) {
2398 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2399 _("Change rectangle"));
2400 }
2402 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2403 }
2405 static void
2406 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2407 {
2408 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2409 }
2411 static void
2412 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2413 {
2414 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2415 }
2417 static void
2418 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2419 {
2420 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2421 }
2423 static void
2424 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2425 {
2426 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2427 }
2431 static void
2432 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2433 {
2434 GtkAdjustment *adj = 0;
2436 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2437 gtk_adjustment_set_value(adj, 0.0);
2438 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2439 gtk_adjustment_value_changed(adj);
2441 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2442 gtk_adjustment_set_value(adj, 0.0);
2443 gtk_adjustment_value_changed(adj);
2445 sp_rtb_sensitivize( obj );
2446 }
2448 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2449 gchar const */*old_value*/, gchar const */*new_value*/,
2450 bool /*is_interactive*/, gpointer data)
2451 {
2452 GObject *tbl = G_OBJECT(data);
2454 // quit if run by the _changed callbacks
2455 if (g_object_get_data( tbl, "freeze" )) {
2456 return;
2457 }
2459 // in turn, prevent callbacks from responding
2460 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2462 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2463 SPUnit const *unit = tracker->getActiveUnit();
2465 gpointer item = g_object_get_data( tbl, "item" );
2466 if (item && SP_IS_RECT(item)) {
2467 {
2468 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2469 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2470 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2471 }
2473 {
2474 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2475 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2476 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2477 }
2479 {
2480 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2481 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2482 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2483 }
2485 {
2486 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2487 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2488 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2489 }
2490 }
2492 sp_rtb_sensitivize( tbl );
2494 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2495 }
2498 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2499 NULL, /* child_added */
2500 NULL, /* child_removed */
2501 rect_tb_event_attr_changed,
2502 NULL, /* content_changed */
2503 NULL /* order_changed */
2504 };
2506 /**
2507 * \param selection should not be NULL.
2508 */
2509 static void
2510 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2511 {
2512 int n_selected = 0;
2513 Inkscape::XML::Node *repr = NULL;
2514 SPItem *item = NULL;
2516 if ( g_object_get_data( tbl, "repr" ) ) {
2517 g_object_set_data( tbl, "item", NULL );
2518 }
2519 purge_repr_listener( tbl, tbl );
2521 for (GSList const *items = selection->itemList();
2522 items != NULL;
2523 items = items->next) {
2524 if (SP_IS_RECT((SPItem *) items->data)) {
2525 n_selected++;
2526 item = (SPItem *) items->data;
2527 repr = SP_OBJECT_REPR(item);
2528 }
2529 }
2531 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2533 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2535 if (n_selected == 0) {
2536 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2538 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2539 gtk_action_set_sensitive(w, FALSE);
2540 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2541 gtk_action_set_sensitive(h, FALSE);
2543 } else if (n_selected == 1) {
2544 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2545 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2547 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2548 gtk_action_set_sensitive(w, TRUE);
2549 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2550 gtk_action_set_sensitive(h, TRUE);
2552 if (repr) {
2553 g_object_set_data( tbl, "repr", repr );
2554 g_object_set_data( tbl, "item", item );
2555 Inkscape::GC::anchor(repr);
2556 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2557 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2558 }
2559 } else {
2560 // FIXME: implement averaging of all parameters for multiple selected
2561 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2562 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2563 sp_rtb_sensitivize( tbl );
2564 }
2565 }
2568 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2569 {
2570 EgeAdjustmentAction* eact = 0;
2571 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2573 {
2574 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2575 ege_output_action_set_use_markup( act, TRUE );
2576 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2577 g_object_set_data( holder, "mode_action", act );
2578 }
2580 // rx/ry units menu: create
2581 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2582 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2583 // fixme: add % meaning per cent of the width/height
2584 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2585 g_object_set_data( holder, "tracker", tracker );
2587 /* W */
2588 {
2589 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2590 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2591 eact = create_adjustment_action( "RectWidthAction",
2592 _("Width"), _("W:"), _("Width of rectangle"),
2593 "tools.shapes.rect", "width", 0,
2594 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2595 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2596 labels, values, G_N_ELEMENTS(labels),
2597 sp_rtb_width_value_changed );
2598 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2599 g_object_set_data( holder, "width_action", eact );
2600 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2601 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2602 }
2604 /* H */
2605 {
2606 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2607 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2608 eact = create_adjustment_action( "RectHeightAction",
2609 _("Height"), _("H:"), _("Height of rectangle"),
2610 "tools.shapes.rect", "height", 0,
2611 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2612 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2613 labels, values, G_N_ELEMENTS(labels),
2614 sp_rtb_height_value_changed );
2615 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2616 g_object_set_data( holder, "height_action", eact );
2617 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2618 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2619 }
2621 /* rx */
2622 {
2623 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2624 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2625 eact = create_adjustment_action( "RadiusXAction",
2626 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2627 "tools.shapes.rect", "rx", 0,
2628 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2629 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2630 labels, values, G_N_ELEMENTS(labels),
2631 sp_rtb_rx_value_changed);
2632 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2633 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2634 }
2636 /* ry */
2637 {
2638 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2639 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2640 eact = create_adjustment_action( "RadiusYAction",
2641 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2642 "tools.shapes.rect", "ry", 0,
2643 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2644 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2645 labels, values, G_N_ELEMENTS(labels),
2646 sp_rtb_ry_value_changed);
2647 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2648 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2649 }
2651 // add the units menu
2652 {
2653 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2654 gtk_action_group_add_action( mainActions, act );
2655 }
2657 /* Reset */
2658 {
2659 InkAction* inky = ink_action_new( "RectResetAction",
2660 _("Not rounded"),
2661 _("Make corners sharp"),
2662 "squared_corner",
2663 secondarySize );
2664 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2665 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2666 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2667 g_object_set_data( holder, "not_rounded", inky );
2668 }
2670 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2671 sp_rtb_sensitivize( holder );
2673 sigc::connection *connection = new sigc::connection(
2674 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2675 );
2676 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2677 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2678 }
2680 //########################
2681 //## 3D Box ##
2682 //########################
2684 // normalize angle so that it lies in the interval [0,360]
2685 static double box3d_normalize_angle (double a) {
2686 double angle = a + ((int) (a/360.0))*360;
2687 if (angle < 0) {
2688 angle += 360.0;
2689 }
2690 return angle;
2691 }
2693 static void
2694 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2695 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2696 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2697 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2698 // are reset).
2699 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2701 if (is_infinite) {
2702 gtk_toggle_action_set_active(tact, TRUE);
2703 gtk_action_set_sensitive(act, TRUE);
2705 double angle = persp3d_get_infinite_angle(persp, axis);
2706 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2707 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2708 }
2709 } else {
2710 gtk_toggle_action_set_active(tact, FALSE);
2711 gtk_action_set_sensitive(act, FALSE);
2712 }
2713 }
2715 static void
2716 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2717 if (!persp_repr) {
2718 g_print ("No perspective given to box3d_resync_toolbar().\n");
2719 return;
2720 }
2722 GtkWidget *tbl = GTK_WIDGET(data);
2723 GtkAdjustment *adj = 0;
2724 GtkAction *act = 0;
2725 GtkToggleAction *tact = 0;
2726 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2727 {
2728 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2729 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2730 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2732 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2733 }
2734 {
2735 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2736 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2737 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2739 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2740 }
2741 {
2742 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2743 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2744 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2746 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2747 }
2748 }
2750 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2751 gchar const */*old_value*/, gchar const */*new_value*/,
2752 bool /*is_interactive*/, gpointer data)
2753 {
2754 GtkWidget *tbl = GTK_WIDGET(data);
2756 // quit if run by the attr_changed listener
2757 // note: it used to work without the differently called freeze_ attributes (here and in
2758 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2759 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2760 return;
2761 }
2763 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2764 // sp_document_maybe_done() when the document is undo insensitive)
2765 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2767 // TODO: Only update the appropriate part of the toolbar
2768 // if (!strcmp(name, "inkscape:vp_z")) {
2769 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2770 // }
2772 Persp3D *persp = persp3d_get_from_repr(repr);
2773 persp3d_update_box_reprs(persp);
2775 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2776 }
2778 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2779 {
2780 NULL, /* child_added */
2781 NULL, /* child_removed */
2782 box3d_persp_tb_event_attr_changed,
2783 NULL, /* content_changed */
2784 NULL /* order_changed */
2785 };
2787 /**
2788 * \param selection Should not be NULL.
2789 */
2790 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2791 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2792 static void
2793 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2794 {
2795 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2796 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2797 // update the perspectives with infinite VPs and leave the other ones untouched).
2799 Inkscape::XML::Node *persp_repr = NULL;
2800 purge_repr_listener(tbl, tbl);
2802 SPItem *item = selection->singleItem();
2803 if (item && SP_IS_BOX3D(item)) {
2804 // FIXME: Also deal with multiple selected boxes
2805 SPBox3D *box = SP_BOX3D(item);
2806 Persp3D *persp = box3d_get_perspective(box);
2807 persp_repr = SP_OBJECT_REPR(persp);
2808 if (persp_repr) {
2809 g_object_set_data(tbl, "repr", persp_repr);
2810 Inkscape::GC::anchor(persp_repr);
2811 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2812 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2813 }
2815 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2816 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2818 box3d_resync_toolbar(persp_repr, tbl);
2819 }
2820 }
2822 static void
2823 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2824 {
2825 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2826 SPDocument *document = sp_desktop_document(desktop);
2828 // quit if run by the attr_changed listener
2829 // note: it used to work without the differently called freeze_ attributes (here and in
2830 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2831 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2832 return;
2833 }
2835 // in turn, prevent listener from responding
2836 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2838 //Persp3D *persp = document->current_persp3d;
2839 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2840 if (sel_persps.empty()) {
2841 // this can happen when the document is created; we silently ignore it
2842 return;
2843 }
2844 Persp3D *persp = sel_persps.front();
2846 persp->tmat.set_infinite_direction (axis, adj->value);
2847 SP_OBJECT(persp)->updateRepr();
2849 // TODO: use the correct axis here, too
2850 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2852 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2853 }
2856 static void
2857 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2858 {
2859 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2860 }
2862 static void
2863 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2864 {
2865 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2866 }
2868 static void
2869 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2870 {
2871 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2872 }
2875 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2876 {
2877 // TODO: Take all selected perspectives into account
2878 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2879 if (sel_persps.empty()) {
2880 // this can happen when the document is created; we silently ignore it
2881 return;
2882 }
2883 Persp3D *persp = sel_persps.front();
2885 bool set_infinite = gtk_toggle_action_get_active(act);
2886 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2887 }
2889 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2890 {
2891 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2892 }
2894 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2895 {
2896 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2897 }
2899 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2900 {
2901 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2902 }
2904 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2905 {
2906 EgeAdjustmentAction* eact = 0;
2907 SPDocument *document = sp_desktop_document (desktop);
2908 Persp3D *persp = document->current_persp3d;
2910 EgeAdjustmentAction* box3d_angle_x = 0;
2911 EgeAdjustmentAction* box3d_angle_y = 0;
2912 EgeAdjustmentAction* box3d_angle_z = 0;
2914 /* Angle X */
2915 {
2916 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2917 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2918 eact = create_adjustment_action( "3DBoxAngleXAction",
2919 _("Angle in X direction"), _("Angle X:"),
2920 // Translators: PL is short for 'perspective line'
2921 _("Angle of PLs in X direction"),
2922 "tools.shapes.3dbox", "box3d_angle_x", 30,
2923 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2924 -360.0, 360.0, 1.0, 10.0,
2925 labels, values, G_N_ELEMENTS(labels),
2926 box3d_angle_x_value_changed );
2927 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2928 g_object_set_data( holder, "box3d_angle_x_action", eact );
2929 box3d_angle_x = eact;
2930 }
2932 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2933 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2934 } else {
2935 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2936 }
2939 /* VP X state */
2940 {
2941 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2942 // Translators: VP is short for 'vanishing point'
2943 _("State of VP in X direction"),
2944 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2945 "toggle_vp_x",
2946 Inkscape::ICON_SIZE_DECORATION );
2947 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2948 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2949 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2950 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2951 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2952 }
2954 /* Angle Y */
2955 {
2956 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2957 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2958 eact = create_adjustment_action( "3DBoxAngleYAction",
2959 _("Angle in Y direction"), _("Angle Y:"),
2960 // Translators: PL is short for 'perspective line'
2961 _("Angle of PLs in Y direction"),
2962 "tools.shapes.3dbox", "box3d_angle_y", 30,
2963 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2964 -360.0, 360.0, 1.0, 10.0,
2965 labels, values, G_N_ELEMENTS(labels),
2966 box3d_angle_y_value_changed );
2967 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2968 g_object_set_data( holder, "box3d_angle_y_action", eact );
2969 box3d_angle_y = eact;
2970 }
2972 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2973 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2974 } else {
2975 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2976 }
2978 /* VP Y state */
2979 {
2980 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2981 // Translators: VP is short for 'vanishing point'
2982 _("State of VP in Y direction"),
2983 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2984 "toggle_vp_y",
2985 Inkscape::ICON_SIZE_DECORATION );
2986 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2987 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2988 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2989 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2990 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2991 }
2993 /* Angle Z */
2994 {
2995 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2996 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2997 eact = create_adjustment_action( "3DBoxAngleZAction",
2998 _("Angle in Z direction"), _("Angle Z:"),
2999 // Translators: PL is short for 'perspective line'
3000 _("Angle of PLs in Z direction"),
3001 "tools.shapes.3dbox", "box3d_angle_z", 30,
3002 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3003 -360.0, 360.0, 1.0, 10.0,
3004 labels, values, G_N_ELEMENTS(labels),
3005 box3d_angle_z_value_changed );
3006 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3007 g_object_set_data( holder, "box3d_angle_z_action", eact );
3008 box3d_angle_z = eact;
3009 }
3011 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3012 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3013 } else {
3014 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3015 }
3017 /* VP Z state */
3018 {
3019 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3020 // Translators: VP is short for 'vanishing point'
3021 _("State of VP in Z direction"),
3022 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3023 "toggle_vp_z",
3024 Inkscape::ICON_SIZE_DECORATION );
3025 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3026 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3027 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3028 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3029 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3030 }
3032 sigc::connection *connection = new sigc::connection(
3033 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3034 );
3035 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3036 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3037 }
3039 //########################
3040 //## Spiral ##
3041 //########################
3043 static void
3044 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3045 {
3046 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3048 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3049 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3050 }
3052 // quit if run by the attr_changed listener
3053 if (g_object_get_data( tbl, "freeze" )) {
3054 return;
3055 }
3057 // in turn, prevent listener from responding
3058 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3060 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3062 bool modmade = false;
3063 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3064 items != NULL;
3065 items = items->next)
3066 {
3067 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3068 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3069 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3070 SP_OBJECT((SPItem *) items->data)->updateRepr();
3071 modmade = true;
3072 }
3073 }
3075 g_free(namespaced_name);
3077 if (modmade) {
3078 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3079 _("Change spiral"));
3080 }
3082 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3083 }
3085 static void
3086 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3087 {
3088 sp_spl_tb_value_changed(adj, tbl, "revolution");
3089 }
3091 static void
3092 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3093 {
3094 sp_spl_tb_value_changed(adj, tbl, "expansion");
3095 }
3097 static void
3098 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3099 {
3100 sp_spl_tb_value_changed(adj, tbl, "t0");
3101 }
3103 static void
3104 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3105 {
3106 GtkWidget *tbl = GTK_WIDGET(obj);
3108 GtkAdjustment *adj;
3110 // fixme: make settable
3111 gdouble rev = 5;
3112 gdouble exp = 1.0;
3113 gdouble t0 = 0.0;
3115 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3116 gtk_adjustment_set_value(adj, rev);
3117 gtk_adjustment_value_changed(adj);
3119 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3120 gtk_adjustment_set_value(adj, exp);
3121 gtk_adjustment_value_changed(adj);
3123 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3124 gtk_adjustment_set_value(adj, t0);
3125 gtk_adjustment_value_changed(adj);
3127 spinbutton_defocus(GTK_OBJECT(tbl));
3128 }
3131 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3132 gchar const */*old_value*/, gchar const */*new_value*/,
3133 bool /*is_interactive*/, gpointer data)
3134 {
3135 GtkWidget *tbl = GTK_WIDGET(data);
3137 // quit if run by the _changed callbacks
3138 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3139 return;
3140 }
3142 // in turn, prevent callbacks from responding
3143 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3145 GtkAdjustment *adj;
3146 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3147 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3149 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3150 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3152 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3153 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3155 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3156 }
3159 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3160 NULL, /* child_added */
3161 NULL, /* child_removed */
3162 spiral_tb_event_attr_changed,
3163 NULL, /* content_changed */
3164 NULL /* order_changed */
3165 };
3167 static void
3168 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3169 {
3170 int n_selected = 0;
3171 Inkscape::XML::Node *repr = NULL;
3173 purge_repr_listener( tbl, tbl );
3175 for (GSList const *items = selection->itemList();
3176 items != NULL;
3177 items = items->next)
3178 {
3179 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3180 n_selected++;
3181 repr = SP_OBJECT_REPR((SPItem *) items->data);
3182 }
3183 }
3185 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3187 if (n_selected == 0) {
3188 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3189 } else if (n_selected == 1) {
3190 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3192 if (repr) {
3193 g_object_set_data( tbl, "repr", repr );
3194 Inkscape::GC::anchor(repr);
3195 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3196 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3197 }
3198 } else {
3199 // FIXME: implement averaging of all parameters for multiple selected
3200 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3201 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3202 }
3203 }
3206 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3207 {
3208 EgeAdjustmentAction* eact = 0;
3209 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3211 {
3212 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3213 ege_output_action_set_use_markup( act, TRUE );
3214 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3215 g_object_set_data( holder, "mode_action", act );
3216 }
3218 /* Revolution */
3219 {
3220 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3221 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3222 eact = create_adjustment_action( "SpiralRevolutionAction",
3223 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3224 "tools.shapes.spiral", "revolution", 3.0,
3225 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3226 0.01, 1024.0, 0.1, 1.0,
3227 labels, values, G_N_ELEMENTS(labels),
3228 sp_spl_tb_revolution_value_changed, 1, 2);
3229 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3230 }
3232 /* Expansion */
3233 {
3234 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3235 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3236 eact = create_adjustment_action( "SpiralExpansionAction",
3237 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3238 "tools.shapes.spiral", "expansion", 1.0,
3239 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3240 0.0, 1000.0, 0.01, 1.0,
3241 labels, values, G_N_ELEMENTS(labels),
3242 sp_spl_tb_expansion_value_changed);
3243 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3244 }
3246 /* T0 */
3247 {
3248 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3249 gdouble values[] = {0, 0.5, 0.9};
3250 eact = create_adjustment_action( "SpiralT0Action",
3251 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3252 "tools.shapes.spiral", "t0", 0.0,
3253 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3254 0.0, 0.999, 0.01, 1.0,
3255 labels, values, G_N_ELEMENTS(labels),
3256 sp_spl_tb_t0_value_changed);
3257 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3258 }
3260 /* Reset */
3261 {
3262 InkAction* inky = ink_action_new( "SpiralResetAction",
3263 _("Defaults"),
3264 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3265 GTK_STOCK_CLEAR,
3266 secondarySize );
3267 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3268 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3269 }
3272 sigc::connection *connection = new sigc::connection(
3273 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3274 );
3275 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3276 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3277 }
3279 //########################
3280 //## Pen/Pencil ##
3281 //########################
3283 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3284 static char const *
3285 freehand_tool_name(GObject *dataKludge)
3286 {
3287 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3288 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3289 ? "tools.freehand.pen"
3290 : "tools.freehand.pencil" );
3291 }
3293 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3294 {
3295 gint mode = ege_select_one_action_get_active(act);
3297 prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3299 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3301 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3302 // preparatory work here
3303 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3304 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3305 sp_pen_context_set_polyline_mode(pc);
3306 }
3307 }
3309 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3310 {
3311 /* Freehand mode toggle buttons */
3312 {
3313 guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3314 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3316 {
3317 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3319 GtkTreeIter iter;
3320 gtk_list_store_append( model, &iter );
3321 gtk_list_store_set( model, &iter,
3322 0, _("Bezier"),
3323 1, _("Create regular Bezier path"),
3324 2, "bezier_mode",
3325 -1 );
3327 gtk_list_store_append( model, &iter );
3328 gtk_list_store_set( model, &iter,
3329 0, _("Spiro"),
3330 1, _("Create Spiro path"),
3331 2, "spiro_splines_mode",
3332 -1 );
3334 if (!tool_is_pencil) {
3335 gtk_list_store_append( model, &iter );
3336 gtk_list_store_set( model, &iter,
3337 0, _("Zigzag"),
3338 1, _("Create a sequence of straight line segments"),
3339 2, "polylines_mode",
3340 -1 );
3342 gtk_list_store_append( model, &iter );
3343 gtk_list_store_set( model, &iter,
3344 0, _("Paraxial"),
3345 1, _("Create a sequence of paraxial line segments"),
3346 2, "paraxial_lines_mode",
3347 -1 );
3348 }
3350 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3351 "FreehandModeActionPencil" :
3352 "FreehandModeActionPen",
3353 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3354 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3356 ege_select_one_action_set_appearance( act, "full" );
3357 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3358 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3359 ege_select_one_action_set_icon_column( act, 2 );
3360 ege_select_one_action_set_icon_size( act, secondarySize );
3361 ege_select_one_action_set_tooltip_column( act, 1 );
3363 ege_select_one_action_set_active( act, freehandMode);
3364 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3365 }
3366 }
3367 }
3369 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3370 gint shape = ege_select_one_action_get_active( act );
3371 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3372 }
3374 /**
3375 * \brief Generate the list of freehand advanced shape option entries.
3376 */
3377 GList * freehand_shape_dropdown_items_list() {
3378 GList *glist = NULL;
3380 glist = g_list_append (glist, _("None"));
3381 glist = g_list_append (glist, _("Triangle in"));
3382 glist = g_list_append (glist, _("Triangle out"));
3383 glist = g_list_append (glist, _("Ellipse"));
3384 glist = g_list_append (glist, _("From clipboard"));
3386 return glist;
3387 }
3389 static void
3390 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3391 /*advanced shape options */
3392 {
3393 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3395 GList* items = 0;
3396 gint count = 0;
3397 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3398 {
3399 GtkTreeIter iter;
3400 gtk_list_store_append( model, &iter );
3401 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3402 count++;
3403 }
3404 g_list_free( items );
3405 items = 0;
3406 EgeSelectOneAction* act1 = ege_select_one_action_new(
3407 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3408 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3409 g_object_set( act1, "short_label", _("Shape:"), NULL );
3410 ege_select_one_action_set_appearance( act1, "compact" );
3411 ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3412 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3413 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3414 g_object_set_data( holder, "shape_action", act1 );
3415 }
3416 }
3418 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3419 {
3420 sp_add_freehand_mode_toggle(mainActions, holder, false);
3421 freehand_add_advanced_shape_options(mainActions, holder, false);
3422 }
3425 static void
3426 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3427 {
3428 GtkWidget *tbl = GTK_WIDGET(obj);
3430 GtkAdjustment *adj;
3432 // fixme: make settable
3433 gdouble tolerance = 4;
3435 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3436 gtk_adjustment_set_value(adj, tolerance);
3437 gtk_adjustment_value_changed(adj);
3439 spinbutton_defocus(GTK_OBJECT(tbl));
3440 }
3442 static void
3443 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3444 {
3446 // quit if run by the attr_changed listener
3447 if (g_object_get_data( tbl, "freeze" )) {
3448 return;
3449 }
3450 // in turn, prevent listener from responding
3451 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3452 prefs_set_double_attribute("tools.freehand.pencil",
3453 "tolerance", adj->value);
3454 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3456 }
3460 static void
3461 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3462 const gchar */*key*/,
3463 const gchar */*oldval*/,
3464 const gchar */*newval*/,
3465 bool /*is_interactive*/,
3466 void * data)
3467 {
3468 GObject* tbl = G_OBJECT(data);
3469 if (g_object_get_data( tbl, "freeze" )) {
3470 return;
3471 }
3473 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3475 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3476 "tolerance");
3478 double v = prefs_get_double_attribute("tools.freehand.pencil",
3479 "tolerance", adj->value);
3480 gtk_adjustment_set_value(adj, v);
3481 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3483 }
3485 static Inkscape::XML::NodeEventVector pencil_node_events =
3486 {
3487 NULL,
3488 NULL,
3489 sp_pencil_tb_tolerance_value_changed_external,
3490 NULL,
3491 NULL,
3492 };
3495 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3496 {
3497 sp_add_freehand_mode_toggle(mainActions, holder, true);
3499 EgeAdjustmentAction* eact = 0;
3501 /* Tolerance */
3502 {
3503 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3504 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3505 eact = create_adjustment_action( "PencilToleranceAction",
3506 _("Smoothing:"), _("Smoothing: "),
3507 _("How much smoothing (simplifying) is applied to the line"),
3508 "tools.freehand.pencil", "tolerance",
3509 3.0,
3510 GTK_WIDGET(desktop->canvas), NULL,
3511 holder, TRUE, "altx-pencil",
3512 1, 100.0, 0.5, 0,
3513 labels, values, G_N_ELEMENTS(labels),
3514 sp_pencil_tb_tolerance_value_changed,
3515 1, 2);
3516 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3517 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3519 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3520 "tools.freehand.pencil");
3521 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3522 g_object_set_data(G_OBJECT(holder), "repr", repr);
3524 }
3526 /* advanced shape options */
3527 freehand_add_advanced_shape_options(mainActions, holder, true);
3529 /* Reset */
3530 {
3531 InkAction* inky = ink_action_new( "PencilResetAction",
3532 _("Defaults"),
3533 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3534 GTK_STOCK_CLEAR,
3535 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3536 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3537 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3538 }
3540 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3542 }
3545 //########################
3546 //## Tweak ##
3547 //########################
3549 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3550 {
3551 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3552 }
3554 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3555 {
3556 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3557 }
3559 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3560 {
3561 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3562 }
3564 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3565 {
3566 int mode = ege_select_one_action_get_active( act );
3567 prefs_set_int_attribute("tools.tweak", "mode", mode);
3569 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3570 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3571 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3572 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3573 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3574 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3575 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3576 if (doh) gtk_action_set_sensitive (doh, TRUE);
3577 if (dos) gtk_action_set_sensitive (dos, TRUE);
3578 if (dol) gtk_action_set_sensitive (dol, TRUE);
3579 if (doo) gtk_action_set_sensitive (doo, TRUE);
3580 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3581 if (fid) gtk_action_set_sensitive (fid, FALSE);
3582 } else {
3583 if (doh) gtk_action_set_sensitive (doh, FALSE);
3584 if (dos) gtk_action_set_sensitive (dos, FALSE);
3585 if (dol) gtk_action_set_sensitive (dol, FALSE);
3586 if (doo) gtk_action_set_sensitive (doo, FALSE);
3587 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3588 if (fid) gtk_action_set_sensitive (fid, TRUE);
3589 }
3590 }
3592 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3593 {
3594 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3595 }
3597 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3598 bool show = gtk_toggle_action_get_active( act );
3599 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3600 }
3601 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3602 bool show = gtk_toggle_action_get_active( act );
3603 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3604 }
3605 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3606 bool show = gtk_toggle_action_get_active( act );
3607 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3608 }
3609 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3610 bool show = gtk_toggle_action_get_active( act );
3611 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3612 }
3614 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3615 {
3616 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3618 {
3619 /* Width */
3620 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3621 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3622 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3623 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3624 "tools.tweak", "width", 15,
3625 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3626 1, 100, 1.0, 0.0,
3627 labels, values, G_N_ELEMENTS(labels),
3628 sp_tweak_width_value_changed, 0.01, 0, 100 );
3629 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3630 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3631 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3632 }
3635 {
3636 /* Force */
3637 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3638 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3639 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3640 _("Force"), _("Force:"), _("The force of the tweak action"),
3641 "tools.tweak", "force", 20,
3642 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3643 1, 100, 1.0, 0.0,
3644 labels, values, G_N_ELEMENTS(labels),
3645 sp_tweak_force_value_changed, 0.01, 0, 100 );
3646 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3647 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3648 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3649 }
3651 /* Mode */
3652 {
3653 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3655 GtkTreeIter iter;
3656 gtk_list_store_append( model, &iter );
3657 gtk_list_store_set( model, &iter,
3658 0, _("Push mode"),
3659 1, _("Push parts of paths in any direction"),
3660 2, "tweak_push_mode",
3661 -1 );
3663 gtk_list_store_append( model, &iter );
3664 gtk_list_store_set( model, &iter,
3665 0, _("Shrink mode"),
3666 1, _("Shrink (inset) parts of paths"),
3667 2, "tweak_shrink_mode",
3668 -1 );
3670 gtk_list_store_append( model, &iter );
3671 gtk_list_store_set( model, &iter,
3672 0, _("Grow mode"),
3673 1, _("Grow (outset) parts of paths"),
3674 2, "tweak_grow_mode",
3675 -1 );
3677 gtk_list_store_append( model, &iter );
3678 gtk_list_store_set( model, &iter,
3679 0, _("Attract mode"),
3680 1, _("Attract parts of paths towards cursor"),
3681 2, "tweak_attract_mode",
3682 -1 );
3684 gtk_list_store_append( model, &iter );
3685 gtk_list_store_set( model, &iter,
3686 0, _("Repel mode"),
3687 1, _("Repel parts of paths from cursor"),
3688 2, "tweak_repel_mode",
3689 -1 );
3691 gtk_list_store_append( model, &iter );
3692 gtk_list_store_set( model, &iter,
3693 0, _("Roughen mode"),
3694 1, _("Roughen parts of paths"),
3695 2, "tweak_roughen_mode",
3696 -1 );
3698 gtk_list_store_append( model, &iter );
3699 gtk_list_store_set( model, &iter,
3700 0, _("Color paint mode"),
3701 1, _("Paint the tool's color upon selected objects"),
3702 2, "tweak_colorpaint_mode",
3703 -1 );
3705 gtk_list_store_append( model, &iter );
3706 gtk_list_store_set( model, &iter,
3707 0, _("Color jitter mode"),
3708 1, _("Jitter the colors of selected objects"),
3709 2, "tweak_colorjitter_mode",
3710 -1 );
3712 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3713 g_object_set( act, "short_label", _("Mode:"), NULL );
3714 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3715 g_object_set_data( holder, "mode_action", act );
3717 ege_select_one_action_set_appearance( act, "full" );
3718 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3719 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3720 ege_select_one_action_set_icon_column( act, 2 );
3721 ege_select_one_action_set_icon_size( act, secondarySize );
3722 ege_select_one_action_set_tooltip_column( act, 1 );
3724 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3725 ege_select_one_action_set_active( act, mode );
3726 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3728 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3729 }
3731 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3733 {
3734 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3735 ege_output_action_set_use_markup( act, TRUE );
3736 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3737 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3738 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3739 g_object_set_data( holder, "tweak_channels_label", act);
3740 }
3742 {
3743 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3744 _("Hue"),
3745 _("In color mode, act on objects' hue"),
3746 NULL,
3747 Inkscape::ICON_SIZE_DECORATION );
3748 //TRANSLATORS: "H" here stands for hue
3749 g_object_set( act, "short_label", _("H"), NULL );
3750 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3751 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3752 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3753 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3754 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3755 g_object_set_data( holder, "tweak_doh", act);
3756 }
3757 {
3758 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3759 _("Saturation"),
3760 _("In color mode, act on objects' saturation"),
3761 NULL,
3762 Inkscape::ICON_SIZE_DECORATION );
3763 //TRANSLATORS: "S" here stands for Saturation
3764 g_object_set( act, "short_label", _("S"), NULL );
3765 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3766 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3767 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3768 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3769 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3770 g_object_set_data( holder, "tweak_dos", act );
3771 }
3772 {
3773 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3774 _("Lightness"),
3775 _("In color mode, act on objects' lightness"),
3776 NULL,
3777 Inkscape::ICON_SIZE_DECORATION );
3778 //TRANSLATORS: "L" here stands for Lightness
3779 g_object_set( act, "short_label", _("L"), NULL );
3780 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3781 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3782 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3783 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3784 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3785 g_object_set_data( holder, "tweak_dol", act );
3786 }
3787 {
3788 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3789 _("Opacity"),
3790 _("In color mode, act on objects' opacity"),
3791 NULL,
3792 Inkscape::ICON_SIZE_DECORATION );
3793 //TRANSLATORS: "O" here stands for Opacity
3794 g_object_set( act, "short_label", _("O"), NULL );
3795 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3796 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3797 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3798 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3799 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3800 g_object_set_data( holder, "tweak_doo", act );
3801 }
3803 { /* Fidelity */
3804 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3805 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3806 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3807 _("Fidelity"), _("Fidelity:"),
3808 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3809 "tools.tweak", "fidelity", 50,
3810 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3811 1, 100, 1.0, 10.0,
3812 labels, values, G_N_ELEMENTS(labels),
3813 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3814 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3815 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3816 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3817 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3818 g_object_set_data( holder, "tweak_fidelity", eact );
3819 }
3822 /* Use Pressure button */
3823 {
3824 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3825 _("Pressure"),
3826 _("Use the pressure of the input device to alter the force of tweak action"),
3827 "use_pressure",
3828 Inkscape::ICON_SIZE_DECORATION );
3829 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3830 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3831 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3832 }
3834 }
3837 //########################
3838 //## Calligraphy ##
3839 //########################
3840 static void update_presets_list (GObject *tbl)
3841 {
3842 if (g_object_get_data(tbl, "presets_blocked"))
3843 return;
3845 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3846 if (!sel) {
3847 ege_select_one_action_set_active(sel, 0);
3848 return;
3849 }
3851 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3853 for (int i = 1; i <= total_prefs; i++) {
3854 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3855 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3857 bool match = true;
3859 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3860 iter;
3861 ++iter ) {
3862 const gchar *attr_name = g_quark_to_string(iter->key);
3863 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3864 continue;
3865 void *widget = g_object_get_data(tbl, attr_name);
3866 if (widget) {
3867 if (GTK_IS_ADJUSTMENT(widget)) {
3868 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3869 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3870 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3871 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3872 match = false;
3873 break;
3874 }
3875 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3876 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3877 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3878 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3879 if (gtk_toggle_action_get_active(toggle) != v) {
3880 match = false;
3881 break;
3882 }
3883 }
3884 }
3885 }
3887 if (match) {
3888 // newly added item is at the same index as the
3889 // save command, so we need to change twice for it to take effect
3890 ege_select_one_action_set_active(sel, 0);
3891 ege_select_one_action_set_active(sel, i);
3892 return;
3893 }
3894 }
3896 // no match found
3897 ege_select_one_action_set_active(sel, 0);
3898 }
3900 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3901 {
3902 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3903 update_presets_list(tbl);
3904 }
3906 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3907 {
3908 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3909 update_presets_list(tbl);
3910 }
3912 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3913 {
3914 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3915 update_presets_list(tbl);
3916 }
3918 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3919 {
3920 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3921 update_presets_list(tbl);
3922 }
3924 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3925 {
3926 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3927 update_presets_list(tbl);
3928 }
3930 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3931 {
3932 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3933 update_presets_list(tbl);
3934 }
3936 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3937 {
3938 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3939 update_presets_list(tbl);
3940 }
3942 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3943 {
3944 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3945 update_presets_list(tbl);
3946 }
3948 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3949 {
3950 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3951 update_presets_list(tbl);
3952 }
3954 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3955 {
3956 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3957 update_presets_list(tbl);
3958 }
3960 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3961 {
3962 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3963 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3964 update_presets_list(tbl);
3965 if (calligraphy_angle )
3966 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3967 }
3970 static gchar const *const widget_names[] = {
3971 "width",
3972 "mass",
3973 "wiggle",
3974 "angle",
3975 "thinning",
3976 "tremor",
3977 "flatness",
3978 "cap_rounding",
3979 "usepressure",
3980 "tracebackground",
3981 "usetilt"
3982 };
3985 static void sp_dcc_build_presets_list(GObject *tbl)
3986 {
3987 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3989 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3990 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3991 gtk_list_store_clear (model);
3993 {
3994 GtkTreeIter iter;
3995 gtk_list_store_append( model, &iter );
3996 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3997 }
3999 //TODO: switch back to prefs API
4000 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4001 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4002 int ii=1;
4003 while (child_repr) {
4004 GtkTreeIter iter;
4005 char *preset_name = (char *) child_repr->attribute("name");
4006 gtk_list_store_append( model, &iter );
4007 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4008 child_repr = sp_repr_next(child_repr);
4009 }
4011 {
4012 GtkTreeIter iter;
4013 gtk_list_store_append( model, &iter );
4014 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4015 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4016 }
4018 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4020 update_presets_list (tbl);
4021 }
4023 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4024 {
4025 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4026 if (! desktop) return;
4028 if (g_object_get_data(tbl, "presets_blocked"))
4029 return;
4031 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4032 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4033 // dialog cancelled
4034 update_presets_list (tbl);
4035 return;
4036 }
4037 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4039 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4040 // empty name entered
4041 update_presets_list (tbl);
4042 return;
4043 }
4045 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4047 int new_index = -1;
4048 gchar *pref_path = NULL;
4049 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4051 for (int i = 1; i <= total_prefs; i++) {
4052 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4053 const gchar *name = prefs_get_string_attribute(path, "name");
4054 if (name && !strcmp(name, profile_name.c_str())) {
4055 // we already have preset with this name, replace its values
4056 new_index = i;
4057 pref_path = g_strdup(path);
4058 break;
4059 }
4060 }
4062 if (new_index == -1) {
4063 // no preset with this name, create
4064 new_index = total_prefs + 1;
4065 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4066 pref_path = create_pref("tools.calligraphic.preset", profile_id);
4067 g_free(profile_id);
4068 }
4070 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4071 gchar const *const widget_name = widget_names[i];
4072 void *widget = g_object_get_data(tbl, widget_name);
4073 if (widget) {
4074 if (GTK_IS_ADJUSTMENT(widget)) {
4075 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4076 double v = gtk_adjustment_get_value(adj);
4077 prefs_set_double_attribute(pref_path, widget_name, v);
4078 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4079 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4080 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4081 int v = gtk_toggle_action_get_active(toggle);
4082 prefs_set_int_attribute(pref_path, widget_name, v);
4083 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4084 } else {
4085 g_warning("Unknown widget type for preset: %s\n", widget_name);
4086 }
4087 } else {
4088 g_warning("Bad key when writing preset: %s\n", widget_name);
4089 }
4090 }
4091 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4093 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4095 sp_dcc_build_presets_list (tbl);
4097 g_free(pref_path);
4098 }
4101 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4103 gint preset_index = ege_select_one_action_get_active( act );
4104 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4106 if (preset_index == save_presets_index) {
4107 // this is the Save command
4108 sp_dcc_save_profile(NULL, tbl);
4109 return;
4110 }
4112 if (g_object_get_data(tbl, "presets_blocked"))
4113 return;
4115 gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4117 if (preset_path) {
4118 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
4120 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4122 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4123 iter;
4124 ++iter ) {
4125 const gchar *attr_name = g_quark_to_string(iter->key);
4126 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4127 continue;
4128 void *widget = g_object_get_data(tbl, attr_name);
4129 if (widget) {
4130 if (GTK_IS_ADJUSTMENT(widget)) {
4131 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4132 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4133 gtk_adjustment_set_value(adj, v);
4134 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4135 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4136 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4137 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4138 gtk_toggle_action_set_active(toggle, v);
4139 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4140 } else {
4141 g_warning("Unknown widget type for preset: %s\n", attr_name);
4142 }
4143 } else {
4144 g_warning("Bad key found in a preset record: %s\n", attr_name);
4145 }
4146 }
4147 g_free(preset_path);
4148 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4149 }
4151 }
4154 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4155 {
4156 {
4157 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4159 EgeAdjustmentAction* calligraphy_angle = 0;
4161 {
4162 /* Width */
4163 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4164 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4165 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4166 _("Pen Width"), _("Width:"),
4167 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4168 "tools.calligraphic", "width", 15,
4169 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4170 1, 100, 1.0, 0.0,
4171 labels, values, G_N_ELEMENTS(labels),
4172 sp_ddc_width_value_changed, 0.01, 0, 100 );
4173 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4174 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4175 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4176 }
4178 {
4179 /* Thinning */
4180 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4181 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4182 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4183 _("Stroke Thinning"), _("Thinning:"),
4184 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4185 "tools.calligraphic", "thinning", 10,
4186 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4187 -100, 100, 1, 0.1,
4188 labels, values, G_N_ELEMENTS(labels),
4189 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4190 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4191 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4192 }
4194 {
4195 /* Angle */
4196 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4197 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4198 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4199 _("Pen Angle"), _("Angle:"),
4200 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4201 "tools.calligraphic", "angle", 30,
4202 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4203 -90.0, 90.0, 1.0, 10.0,
4204 labels, values, G_N_ELEMENTS(labels),
4205 sp_ddc_angle_value_changed, 1, 0 );
4206 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4207 g_object_set_data( holder, "angle_action", eact );
4208 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4209 calligraphy_angle = eact;
4210 }
4212 {
4213 /* Fixation */
4214 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4215 gdouble values[] = {0, 20, 40, 60, 90, 100};
4216 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4217 _("Fixation"), _("Fixation:"),
4218 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4219 "tools.calligraphic", "flatness", 90,
4220 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4221 0.0, 100, 1.0, 10.0,
4222 labels, values, G_N_ELEMENTS(labels),
4223 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4224 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4225 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4226 }
4228 {
4229 /* Cap Rounding */
4230 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4231 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4232 // TRANSLATORS: "cap" means "end" (both start and finish) here
4233 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4234 _("Cap rounding"), _("Caps:"),
4235 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4236 "tools.calligraphic", "cap_rounding", 0.0,
4237 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4238 0.0, 5.0, 0.01, 0.1,
4239 labels, values, G_N_ELEMENTS(labels),
4240 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4241 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4242 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4243 }
4245 {
4246 /* Tremor */
4247 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4248 gdouble values[] = {0, 10, 20, 40, 60, 100};
4249 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4250 _("Stroke Tremor"), _("Tremor:"),
4251 _("Increase to make strokes rugged and trembling"),
4252 "tools.calligraphic", "tremor", 0.0,
4253 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4254 0.0, 100, 1, 0.0,
4255 labels, values, G_N_ELEMENTS(labels),
4256 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4258 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4259 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4260 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4261 }
4263 {
4264 /* Wiggle */
4265 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4266 gdouble values[] = {0, 20, 40, 60, 100};
4267 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4268 _("Pen Wiggle"), _("Wiggle:"),
4269 _("Increase to make the pen waver and wiggle"),
4270 "tools.calligraphic", "wiggle", 0.0,
4271 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4272 0.0, 100, 1, 0.0,
4273 labels, values, G_N_ELEMENTS(labels),
4274 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4275 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4276 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4277 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4278 }
4280 {
4281 /* Mass */
4282 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4283 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4284 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4285 _("Pen Mass"), _("Mass:"),
4286 _("Increase to make the pen drag behind, as if slowed by inertia"),
4287 "tools.calligraphic", "mass", 2.0,
4288 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4289 0.0, 100, 1, 0.0,
4290 labels, values, G_N_ELEMENTS(labels),
4291 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4292 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4293 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4294 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4295 }
4298 /* Trace Background button */
4299 {
4300 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4301 _("Trace Background"),
4302 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4303 "trace_background",
4304 Inkscape::ICON_SIZE_DECORATION );
4305 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4306 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4307 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4308 g_object_set_data( holder, "tracebackground", act );
4309 }
4311 /* Use Pressure button */
4312 {
4313 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4314 _("Pressure"),
4315 _("Use the pressure of the input device to alter the width of the pen"),
4316 "use_pressure",
4317 Inkscape::ICON_SIZE_DECORATION );
4318 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4319 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4320 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4321 g_object_set_data( holder, "usepressure", act );
4322 }
4324 /* Use Tilt button */
4325 {
4326 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4327 _("Tilt"),
4328 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4329 "use_tilt",
4330 Inkscape::ICON_SIZE_DECORATION );
4331 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4332 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4333 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4334 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4335 g_object_set_data( holder, "usetilt", act );
4336 }
4338 /*calligraphic profile */
4339 {
4340 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4341 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4342 ege_select_one_action_set_appearance (act1, "compact");
4343 g_object_set_data (holder, "profile_selector", act1 );
4345 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4347 sp_dcc_build_presets_list (holder);
4349 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4350 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4351 }
4352 }
4353 }
4356 //########################
4357 //## Circle / Arc ##
4358 //########################
4360 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4361 {
4362 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4363 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4365 if (v1 == 0 && v2 == 0) {
4366 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4367 gtk_action_set_sensitive( ocb, FALSE );
4368 gtk_action_set_sensitive( make_whole, FALSE );
4369 }
4370 } else {
4371 gtk_action_set_sensitive( ocb, TRUE );
4372 gtk_action_set_sensitive( make_whole, TRUE );
4373 }
4374 }
4376 static void
4377 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4378 {
4379 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4381 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4382 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4383 }
4385 // quit if run by the attr_changed listener
4386 if (g_object_get_data( tbl, "freeze" )) {
4387 return;
4388 }
4390 // in turn, prevent listener from responding
4391 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4393 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4395 bool modmade = false;
4396 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4397 items != NULL;
4398 items = items->next)
4399 {
4400 SPItem *item = SP_ITEM(items->data);
4402 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4404 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4405 SPArc *arc = SP_ARC(item);
4407 if (!strcmp(value_name, "start"))
4408 ge->start = (adj->value * M_PI)/ 180;
4409 else
4410 ge->end = (adj->value * M_PI)/ 180;
4412 sp_genericellipse_normalize(ge);
4413 ((SPObject *)arc)->updateRepr();
4414 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4416 modmade = true;
4417 }
4418 }
4420 g_free(namespaced_name);
4422 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4424 sp_arctb_sensitivize( tbl, adj->value, other->value );
4426 if (modmade) {
4427 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4428 _("Arc: Change start/end"));
4429 }
4431 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4432 }
4435 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4436 {
4437 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4438 }
4440 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4441 {
4442 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4443 }
4446 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4447 {
4448 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4449 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4450 if ( ege_select_one_action_get_active( act ) != 0 ) {
4451 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4452 } else {
4453 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4454 }
4455 }
4457 // quit if run by the attr_changed listener
4458 if (g_object_get_data( tbl, "freeze" )) {
4459 return;
4460 }
4462 // in turn, prevent listener from responding
4463 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4465 bool modmade = false;
4467 if ( ege_select_one_action_get_active(act) != 0 ) {
4468 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4469 items != NULL;
4470 items = items->next)
4471 {
4472 if (SP_IS_ARC((SPItem *) items->data)) {
4473 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4474 repr->setAttribute("sodipodi:open", "true");
4475 SP_OBJECT((SPItem *) items->data)->updateRepr();
4476 modmade = true;
4477 }
4478 }
4479 } else {
4480 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4481 items != NULL;
4482 items = items->next)
4483 {
4484 if (SP_IS_ARC((SPItem *) items->data)) {
4485 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4486 repr->setAttribute("sodipodi:open", NULL);
4487 SP_OBJECT((SPItem *) items->data)->updateRepr();
4488 modmade = true;
4489 }
4490 }
4491 }
4493 if (modmade) {
4494 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4495 _("Arc: Change open/closed"));
4496 }
4498 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4499 }
4501 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4502 {
4503 GtkAdjustment *adj;
4504 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4505 gtk_adjustment_set_value(adj, 0.0);
4506 gtk_adjustment_value_changed(adj);
4508 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4509 gtk_adjustment_set_value(adj, 0.0);
4510 gtk_adjustment_value_changed(adj);
4512 spinbutton_defocus( GTK_OBJECT(obj) );
4513 }
4515 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4516 gchar const */*old_value*/, gchar const */*new_value*/,
4517 bool /*is_interactive*/, gpointer data)
4518 {
4519 GObject *tbl = G_OBJECT(data);
4521 // quit if run by the _changed callbacks
4522 if (g_object_get_data( tbl, "freeze" )) {
4523 return;
4524 }
4526 // in turn, prevent callbacks from responding
4527 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4529 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4530 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4532 GtkAdjustment *adj1,*adj2;
4533 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4534 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4535 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4536 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4538 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4540 char const *openstr = NULL;
4541 openstr = repr->attribute("sodipodi:open");
4542 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4544 if (openstr) {
4545 ege_select_one_action_set_active( ocb, 1 );
4546 } else {
4547 ege_select_one_action_set_active( ocb, 0 );
4548 }
4550 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4551 }
4553 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4554 NULL, /* child_added */
4555 NULL, /* child_removed */
4556 arc_tb_event_attr_changed,
4557 NULL, /* content_changed */
4558 NULL /* order_changed */
4559 };
4562 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4563 {
4564 int n_selected = 0;
4565 Inkscape::XML::Node *repr = NULL;
4567 purge_repr_listener( tbl, tbl );
4569 for (GSList const *items = selection->itemList();
4570 items != NULL;
4571 items = items->next)
4572 {
4573 if (SP_IS_ARC((SPItem *) items->data)) {
4574 n_selected++;
4575 repr = SP_OBJECT_REPR((SPItem *) items->data);
4576 }
4577 }
4579 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4581 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4582 if (n_selected == 0) {
4583 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4584 } else if (n_selected == 1) {
4585 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4586 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4588 if (repr) {
4589 g_object_set_data( tbl, "repr", repr );
4590 Inkscape::GC::anchor(repr);
4591 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4592 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4593 }
4594 } else {
4595 // FIXME: implement averaging of all parameters for multiple selected
4596 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4597 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4598 sp_arctb_sensitivize( tbl, 1, 0 );
4599 }
4600 }
4603 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4604 {
4605 EgeAdjustmentAction* eact = 0;
4606 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4609 {
4610 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4611 ege_output_action_set_use_markup( act, TRUE );
4612 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4613 g_object_set_data( holder, "mode_action", act );
4614 }
4616 /* Start */
4617 {
4618 eact = create_adjustment_action( "ArcStartAction",
4619 _("Start"), _("Start:"),
4620 _("The angle (in degrees) from the horizontal to the arc's start point"),
4621 "tools.shapes.arc", "start", 0.0,
4622 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4623 -360.0, 360.0, 1.0, 10.0,
4624 0, 0, 0,
4625 sp_arctb_start_value_changed);
4626 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4627 }
4629 /* End */
4630 {
4631 eact = create_adjustment_action( "ArcEndAction",
4632 _("End"), _("End:"),
4633 _("The angle (in degrees) from the horizontal to the arc's end point"),
4634 "tools.shapes.arc", "end", 0.0,
4635 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4636 -360.0, 360.0, 1.0, 10.0,
4637 0, 0, 0,
4638 sp_arctb_end_value_changed);
4639 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4640 }
4642 /* Segments / Pie checkbox */
4643 {
4644 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4646 GtkTreeIter iter;
4647 gtk_list_store_append( model, &iter );
4648 gtk_list_store_set( model, &iter,
4649 0, _("Closed arc"),
4650 1, _("Switch to segment (closed shape with two radii)"),
4651 2, "circle_closed_arc",
4652 -1 );
4654 gtk_list_store_append( model, &iter );
4655 gtk_list_store_set( model, &iter,
4656 0, _("Open Arc"),
4657 1, _("Switch to arc (unclosed shape)"),
4658 2, "circle_open_arc",
4659 -1 );
4661 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4662 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4663 g_object_set_data( holder, "open_action", act );
4665 ege_select_one_action_set_appearance( act, "full" );
4666 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4667 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4668 ege_select_one_action_set_icon_column( act, 2 );
4669 ege_select_one_action_set_icon_size( act, secondarySize );
4670 ege_select_one_action_set_tooltip_column( act, 1 );
4672 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4673 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4674 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4675 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4676 }
4678 /* Make Whole */
4679 {
4680 InkAction* inky = ink_action_new( "ArcResetAction",
4681 _("Make whole"),
4682 _("Make the shape a whole ellipse, not arc or segment"),
4683 "reset_circle",
4684 secondarySize );
4685 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4686 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4687 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4688 g_object_set_data( holder, "make_whole", inky );
4689 }
4691 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4692 // sensitivize make whole and open checkbox
4693 {
4694 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4695 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4696 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4697 }
4700 sigc::connection *connection = new sigc::connection(
4701 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4702 );
4703 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4704 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4705 }
4710 // toggle button callbacks and updaters
4712 //########################
4713 //## Dropper ##
4714 //########################
4716 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4717 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4718 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4719 if ( set_action ) {
4720 if ( gtk_toggle_action_get_active( act ) ) {
4721 gtk_action_set_sensitive( set_action, TRUE );
4722 } else {
4723 gtk_action_set_sensitive( set_action, FALSE );
4724 }
4725 }
4727 spinbutton_defocus(GTK_OBJECT(tbl));
4728 }
4730 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4731 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4732 spinbutton_defocus(GTK_OBJECT(tbl));
4733 }
4736 /**
4737 * Dropper auxiliary toolbar construction and setup.
4738 *
4739 * TODO: Would like to add swatch of current color.
4740 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4741 * can drag and drop places. Will provide a nice mixing palette.
4742 */
4743 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4744 {
4745 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4747 {
4748 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4749 ege_output_action_set_use_markup( act, TRUE );
4750 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4751 }
4753 {
4754 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4755 _("Pick opacity"),
4756 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4757 NULL,
4758 Inkscape::ICON_SIZE_DECORATION );
4759 g_object_set( act, "short_label", _("Pick"), NULL );
4760 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4761 g_object_set_data( holder, "pick_action", act );
4762 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4763 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4764 }
4766 {
4767 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4768 _("Assign opacity"),
4769 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4770 NULL,
4771 Inkscape::ICON_SIZE_DECORATION );
4772 g_object_set( act, "short_label", _("Assign"), NULL );
4773 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4774 g_object_set_data( holder, "set_action", act );
4775 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4776 // make sure it's disabled if we're not picking alpha
4777 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4778 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4779 }
4780 }
4783 //########################
4784 //## LPETool ##
4785 //########################
4787 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4789 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4790 {
4791 using namespace Inkscape::LivePathEffect;
4793 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4795 // TODO: how can we set *all* actions inactive (such that no sutool is activated?)
4796 gint lpeToolMode = ege_select_one_action_get_active(act);
4797 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4798 prefs_set_int_attribute( "tools.lpetool", "mode", lpeToolMode );
4799 }
4800 EffectType type = lpesubtools[lpeToolMode];
4801 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
4803 // only take action if run by the attr_changed listener
4804 if (!g_object_get_data( tbl, "freeze" )) {
4805 // in turn, prevent listener from responding
4806 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4808 // this is now done in sp_lpetool_context_root_handler()
4809 /**
4810 // activate the LPE corresponding to the chosen subtool
4811 if (type != INVALID_LPE) {
4812 //lc->tool_state = LPETOOL_STATE_PEN;
4813 sp_pen_context_wait_for_LPE_mouse_clicks(pc, type, Effect::acceptsNumClicks(type));
4814 }
4815 // TODO: how can we take LPEs into account that don't expect any 'pre-clicks'?
4816 **/
4818 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4819 }
4820 }
4822 static void
4823 sp_lpetool_test_value_changed(GtkAdjustment *adj, GObject *tbl)
4824 {
4825 g_print ("sp_lpetool_test_value_changed()\n");
4826 using namespace Inkscape::LivePathEffect;
4828 // quit if run by the attr_changed listener
4829 if (g_object_get_data( tbl, "freeze" )) {
4830 return;
4831 }
4833 // in turn, prevent listener from responding
4834 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
4836 LPEAngleBisector *lpeab = static_cast<LPEAngleBisector *>(g_object_get_data(tbl, "currentlpe"));
4837 if (!lpeab) {
4838 g_print ("no LPE!\n");
4839 } else {
4840 g_print ("LPE found. Adjusting left length\n");
4841 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4842 lpeab->length_left.param_set_value(gtk_adjustment_get_value(adj));
4843 sp_lpe_item_update_patheffect(lpeitem, true, true);
4844 }
4846 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4847 }
4849 void
4850 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4851 {
4852 using namespace Inkscape::LivePathEffect;
4853 g_print ("sp_lpetool_toolbox_sel_changed()");
4854 {
4855 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "lpetool_test_action" ) );
4856 SPItem *item = selection->singleItem();
4857 if (item && SP_IS_LPE_ITEM(item)) {
4858 g_print (" - item found\n");
4859 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4860 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4861 if (lpe && lpe->effectType() == ANGLE_BISECTOR) {
4862 LPEAngleBisector *lpeab = dynamic_cast<LPEAngleBisector*>(lpe);
4863 g_object_set_data(tbl, "currentlpe", lpeab);
4864 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4865 gtk_action_set_sensitive(w, TRUE);
4866 } else {
4867 g_object_set_data(tbl, "currentlpe", NULL);
4868 g_object_set_data(tbl, "currentlpeitem", NULL);
4869 gtk_action_set_sensitive(w, FALSE);
4870 }
4871 } else {
4872 g_print (" - unsetting item\n");
4873 g_object_set_data(tbl, "currentlpe", NULL);
4874 g_object_set_data(tbl, "currentlpeitem", NULL);
4875 gtk_action_set_sensitive(w, FALSE);
4876 }
4877 }
4878 g_print ("\n");
4879 }
4881 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4882 {
4883 /** Automatically create a list of LPEs that get added to the toolbar **/
4884 {
4885 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4887 GtkTreeIter iter;
4889 Inkscape::LivePathEffect::EffectType type;
4890 for (int i = 0; i < num_subtools; ++i) {
4891 type = lpesubtools[i];
4892 gtk_list_store_append( model, &iter );
4893 gtk_list_store_set( model, &iter,
4894 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4895 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4896 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4897 -1 );
4898 }
4900 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4901 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4902 g_object_set_data( holder, "lpetool_mode_action", act );
4904 ege_select_one_action_set_appearance( act, "full" );
4905 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4906 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4907 ege_select_one_action_set_icon_column( act, 2 );
4908 ege_select_one_action_set_tooltip_column( act, 1 );
4910 gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4911 ege_select_one_action_set_active( act, lpeToolMode );
4912 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4913 }
4915 /* Test action */
4916 /**
4917 {
4918 EgeAdjustmentAction* eact = 0;
4919 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
4920 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
4921 eact = create_adjustment_action( "TestLPEAction",
4922 _("Test value"), _("Test value:"), _("Test for interactive control widgets ..."),
4923 "tools.lpetool", "testvalue", 0,
4924 GTK_WIDGET(desktop->canvas), NULL us, holder, TRUE, "altx-lpetool",
4925 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
4926 labels, values, G_N_ELEMENTS(labels),
4927 sp_lpetool_test_value_changed );
4928 //tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4929 g_object_set_data( holder, "lpetool_test_action", eact );
4930 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
4931 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4932 }
4933 **/
4935 //watch selection
4936 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
4938 sigc::connection *c_selection_changed =
4939 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4940 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
4941 pool->add_connection ("selection-changed", c_selection_changed);
4942 }
4944 //########################
4945 //## Eraser ##
4946 //########################
4948 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4949 {
4950 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4951 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4952 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4953 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4954 }
4956 // only take action if run by the attr_changed listener
4957 if (!g_object_get_data( tbl, "freeze" )) {
4958 // in turn, prevent listener from responding
4959 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4961 if ( eraserMode != 0 ) {
4962 } else {
4963 }
4964 // TODO finish implementation
4966 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4967 }
4968 }
4970 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4971 {
4972 {
4973 /* Width */
4974 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4975 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4976 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4977 _("Pen Width"), _("Width:"),
4978 _("The width of the eraser pen (relative to the visible canvas area)"),
4979 "tools.eraser", "width", 15,
4980 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4981 1, 100, 1.0, 0.0,
4982 labels, values, G_N_ELEMENTS(labels),
4983 sp_ddc_width_value_changed, 0.01, 0, 100 );
4984 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4985 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4986 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4987 }
4989 {
4990 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4992 GtkTreeIter iter;
4993 gtk_list_store_append( model, &iter );
4994 gtk_list_store_set( model, &iter,
4995 0, _("Delete"),
4996 1, _("Delete objects touched by the eraser"),
4997 2, "delete_object",
4998 -1 );
5000 gtk_list_store_append( model, &iter );
5001 gtk_list_store_set( model, &iter,
5002 0, _("Cut"),
5003 1, _("Cut out from objects"),
5004 2, "difference",
5005 -1 );
5007 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5008 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5009 g_object_set_data( holder, "eraser_mode_action", act );
5011 ege_select_one_action_set_appearance( act, "full" );
5012 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5013 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5014 ege_select_one_action_set_icon_column( act, 2 );
5015 ege_select_one_action_set_tooltip_column( act, 1 );
5017 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5018 ege_select_one_action_set_active( act, eraserMode );
5019 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5020 }
5022 }
5024 //########################
5025 //## Text Toolbox ##
5026 //########################
5027 /*
5028 static void
5029 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5030 {
5031 //Call back for letter sizing spinbutton
5032 }
5034 static void
5035 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5036 {
5037 //Call back for line height spinbutton
5038 }
5040 static void
5041 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5042 {
5043 //Call back for horizontal kerning spinbutton
5044 }
5046 static void
5047 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5048 {
5049 //Call back for vertical kerning spinbutton
5050 }
5052 static void
5053 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5054 {
5055 //Call back for letter rotation spinbutton
5056 }*/
5058 namespace {
5060 bool popdown_visible = false;
5061 bool popdown_hasfocus = false;
5063 void
5064 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5065 {
5066 SPStyle *query =
5067 sp_style_new (SP_ACTIVE_DOCUMENT);
5069 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5071 int result_family =
5072 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5074 int result_style =
5075 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5077 int result_numbers =
5078 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5080 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5082 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5083 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5084 // there are no texts in selection, read from prefs
5086 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5087 if (repr) {
5088 sp_style_read_from_repr (query, repr);
5089 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5090 // do not reset the toolbar style from prefs if we already did it last time
5091 sp_style_unref(query);
5092 return;
5093 }
5094 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5095 } else {
5096 sp_style_unref(query);
5097 return;
5098 }
5099 } else {
5100 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5101 }
5103 if (query->text)
5104 {
5105 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5106 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5107 gtk_entry_set_text (GTK_ENTRY (entry), "");
5109 } else if (query->text->font_specification.value || query->text->font_family.value) {
5111 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5113 // Get the font that corresponds
5114 Glib::ustring familyName;
5116 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5117 if (font) {
5118 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5119 font->Unref();
5120 font = NULL;
5121 }
5123 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5125 Gtk::TreePath path;
5126 try {
5127 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5128 } catch (...) {
5129 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5130 sp_style_unref(query);
5131 return;
5132 }
5134 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5135 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5137 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5139 gtk_tree_selection_select_path (tselection, path.gobj());
5140 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5142 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5143 }
5145 //Size
5146 {
5147 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5148 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5149 g_object_set_data(tbl, "size-block", gpointer(1));
5150 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5151 g_object_set_data(tbl, "size-block", gpointer(0));
5152 g_free(str);
5153 }
5155 //Anchor
5156 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5157 {
5158 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5159 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5160 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5161 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5162 }
5163 else
5164 {
5165 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5166 {
5167 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5168 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5169 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5170 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5171 }
5172 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5173 {
5174 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5175 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5176 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5177 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5178 }
5179 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5180 {
5181 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5182 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5183 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5184 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5185 }
5186 }
5188 //Style
5189 {
5190 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5192 gboolean active = gtk_toggle_button_get_active (button);
5193 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5195 if (active != check)
5196 {
5197 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5198 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5199 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5200 }
5201 }
5203 {
5204 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5206 gboolean active = gtk_toggle_button_get_active (button);
5207 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5209 if (active != check)
5210 {
5211 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5212 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5213 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5214 }
5215 }
5217 //Orientation
5218 //locking both buttons, changing one affect all group (both)
5219 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5220 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5222 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5223 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5225 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5226 {
5227 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5228 }
5229 else
5230 {
5231 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5232 }
5233 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5234 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5235 }
5237 sp_style_unref(query);
5238 }
5240 void
5241 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5242 {
5243 sp_text_toolbox_selection_changed (selection, tbl);
5244 }
5246 void
5247 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5248 {
5249 sp_text_toolbox_selection_changed (NULL, tbl);
5250 }
5252 void
5253 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5254 GObject *tbl)
5255 {
5256 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5257 GtkTreeModel *model = 0;
5258 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5259 GtkTreeIter iter;
5260 char *family = 0;
5262 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5263 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5265 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5266 return;
5267 }
5269 gtk_tree_model_get (model, &iter, 0, &family, -1);
5271 if (g_object_get_data (G_OBJECT (selection), "block"))
5272 {
5273 gtk_entry_set_text (GTK_ENTRY (entry), family);
5274 return;
5275 }
5277 gtk_entry_set_text (GTK_ENTRY (entry), family);
5279 SPStyle *query =
5280 sp_style_new (SP_ACTIVE_DOCUMENT);
5282 int result_fontspec =
5283 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5285 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5287 SPCSSAttr *css = sp_repr_css_attr_new ();
5290 // First try to get the font spec from the stored value
5291 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5293 if (fontSpec.empty()) {
5294 // Construct a new font specification if it does not yet exist
5295 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5296 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5297 fontFromStyle->Unref();
5298 }
5300 if (!fontSpec.empty()) {
5301 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5302 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5303 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5304 if (font) {
5305 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5307 // Set all the these just in case they were altered when finding the best
5308 // match for the new family and old style...
5310 gchar c[256];
5312 font->Family(c, 256);
5313 sp_repr_css_set_property (css, "font-family", c);
5315 font->Attribute( "weight", c, 256);
5316 sp_repr_css_set_property (css, "font-weight", c);
5318 font->Attribute("style", c, 256);
5319 sp_repr_css_set_property (css, "font-style", c);
5321 font->Attribute("stretch", c, 256);
5322 sp_repr_css_set_property (css, "font-stretch", c);
5324 font->Attribute("variant", c, 256);
5325 sp_repr_css_set_property (css, "font-variant", c);
5327 font->Unref();
5328 }
5329 }
5330 }
5332 // If querying returned nothing, set the default style of the tool (for new texts)
5333 if (result_fontspec == QUERY_STYLE_NOTHING)
5334 {
5335 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5336 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5337 }
5338 else
5339 {
5340 sp_desktop_set_style (desktop, css, true, true);
5341 }
5343 sp_style_unref(query);
5345 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5346 _("Text: Change font family"));
5347 sp_repr_css_attr_unref (css);
5348 g_free(family);
5349 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5351 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5352 }
5354 /* This is where execution comes when the contents of the font family box have been completed
5355 by the press of the return key */
5356 void
5357 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5358 GObject *tbl)
5359 {
5360 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5362 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5363 try {
5364 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5365 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5366 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5367 gtk_tree_selection_select_path (selection, path.gobj());
5368 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5369 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5370 } catch (...) {
5371 if (family && strlen (family))
5372 {
5373 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5374 }
5375 }
5376 }
5378 void
5379 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5380 gpointer data)
5381 {
5382 if (g_object_get_data (G_OBJECT (button), "block")) return;
5383 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5384 int prop = GPOINTER_TO_INT(data);
5386 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5387 SPCSSAttr *css = sp_repr_css_attr_new ();
5389 switch (prop)
5390 {
5391 case 0:
5392 {
5393 sp_repr_css_set_property (css, "text-anchor", "start");
5394 sp_repr_css_set_property (css, "text-align", "start");
5395 break;
5396 }
5397 case 1:
5398 {
5399 sp_repr_css_set_property (css, "text-anchor", "middle");
5400 sp_repr_css_set_property (css, "text-align", "center");
5401 break;
5402 }
5404 case 2:
5405 {
5406 sp_repr_css_set_property (css, "text-anchor", "end");
5407 sp_repr_css_set_property (css, "text-align", "end");
5408 break;
5409 }
5411 case 3:
5412 {
5413 sp_repr_css_set_property (css, "text-anchor", "start");
5414 sp_repr_css_set_property (css, "text-align", "justify");
5415 break;
5416 }
5417 }
5419 SPStyle *query =
5420 sp_style_new (SP_ACTIVE_DOCUMENT);
5421 int result_numbers =
5422 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5424 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5425 if (result_numbers == QUERY_STYLE_NOTHING)
5426 {
5427 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5428 }
5430 sp_style_unref(query);
5432 sp_desktop_set_style (desktop, css, true, true);
5433 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5434 _("Text: Change alignment"));
5435 sp_repr_css_attr_unref (css);
5437 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5438 }
5440 void
5441 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5442 gpointer data)
5443 {
5444 if (g_object_get_data (G_OBJECT (button), "block")) return;
5446 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5447 SPCSSAttr *css = sp_repr_css_attr_new ();
5448 int prop = GPOINTER_TO_INT(data);
5449 bool active = gtk_toggle_button_get_active (button);
5451 SPStyle *query =
5452 sp_style_new (SP_ACTIVE_DOCUMENT);
5454 int result_fontspec =
5455 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5457 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5458 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5459 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5461 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5462 Glib::ustring newFontSpec = "";
5464 if (fontSpec.empty()) {
5465 // Construct a new font specification if it does not yet exist
5466 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5467 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5468 fontFromStyle->Unref();
5469 }
5471 switch (prop)
5472 {
5473 case 0:
5474 {
5475 if (!fontSpec.empty()) {
5476 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5477 }
5478 if (fontSpec != newFontSpec) {
5479 // Don't even set the bold if the font didn't exist on the system
5480 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5481 }
5482 break;
5483 }
5485 case 1:
5486 {
5487 if (!fontSpec.empty()) {
5488 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5489 }
5490 if (fontSpec != newFontSpec) {
5491 // Don't even set the italic if the font didn't exist on the system
5492 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5493 }
5494 break;
5495 }
5496 }
5498 if (!newFontSpec.empty()) {
5499 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5500 }
5502 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5503 if (result_fontspec == QUERY_STYLE_NOTHING)
5504 {
5505 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5506 }
5508 sp_style_unref(query);
5510 sp_desktop_set_style (desktop, css, true, true);
5511 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5512 _("Text: Change font style"));
5513 sp_repr_css_attr_unref (css);
5515 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5516 }
5518 void
5519 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5520 gpointer data)
5521 {
5522 if (g_object_get_data (G_OBJECT (button), "block")) {
5523 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5524 return;
5525 }
5527 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5528 SPCSSAttr *css = sp_repr_css_attr_new ();
5529 int prop = GPOINTER_TO_INT(data);
5531 switch (prop)
5532 {
5533 case 0:
5534 {
5535 sp_repr_css_set_property (css, "writing-mode", "lr");
5536 break;
5537 }
5539 case 1:
5540 {
5541 sp_repr_css_set_property (css, "writing-mode", "tb");
5542 break;
5543 }
5544 }
5546 SPStyle *query =
5547 sp_style_new (SP_ACTIVE_DOCUMENT);
5548 int result_numbers =
5549 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5551 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5552 if (result_numbers == QUERY_STYLE_NOTHING)
5553 {
5554 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5555 }
5557 sp_desktop_set_style (desktop, css, true, true);
5558 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5559 _("Text: Change orientation"));
5560 sp_repr_css_attr_unref (css);
5562 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5563 }
5565 gboolean
5566 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5567 {
5568 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5569 if (!desktop) return FALSE;
5571 switch (get_group0_keyval (event)) {
5572 case GDK_Escape: // defocus
5573 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5574 sp_text_toolbox_selection_changed (NULL, tbl); // update
5575 return TRUE; // I consumed the event
5576 break;
5577 }
5578 return FALSE;
5579 }
5581 gboolean
5582 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5583 {
5584 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5585 if (!desktop) return FALSE;
5587 switch (get_group0_keyval (event)) {
5588 case GDK_KP_Enter:
5589 case GDK_Return:
5590 case GDK_Escape: // defocus
5591 gtk_widget_hide (w);
5592 popdown_visible = false;
5593 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5594 return TRUE; // I consumed the event
5595 break;
5596 case GDK_w:
5597 case GDK_W:
5598 if (event->state & GDK_CONTROL_MASK) {
5599 gtk_widget_hide (w);
5600 popdown_visible = false;
5601 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5602 return TRUE; // I consumed the event
5603 }
5604 break;
5605 }
5606 return FALSE;
5607 }
5610 void
5611 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5612 GObject *tbl)
5613 {
5614 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5616 if (g_object_get_data (tbl, "size-block")) return;
5618 // If this is not from selecting a size in the list (in which case get_active will give the
5619 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5620 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5621 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5622 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5623 return;
5625 gdouble value = -1;
5626 {
5627 gchar *endptr;
5628 gchar *const text = gtk_combo_box_get_active_text(cbox);
5629 if (text) {
5630 value = g_strtod(text, &endptr);
5631 if (endptr == text) { // Conversion failed, non-numeric input.
5632 value = -1;
5633 }
5634 g_free(text);
5635 }
5636 }
5637 if (value <= 0) {
5638 return; // could not parse value
5639 }
5641 SPCSSAttr *css = sp_repr_css_attr_new ();
5642 Inkscape::CSSOStringStream osfs;
5643 osfs << value;
5644 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5646 SPStyle *query =
5647 sp_style_new (SP_ACTIVE_DOCUMENT);
5648 int result_numbers =
5649 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5651 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5652 if (result_numbers == QUERY_STYLE_NOTHING)
5653 {
5654 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5655 }
5657 sp_style_unref(query);
5659 sp_desktop_set_style (desktop, css, true, true);
5660 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5661 _("Text: Change font size"));
5662 sp_repr_css_attr_unref (css);
5664 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5665 }
5667 gboolean
5668 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5669 {
5670 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5671 if (!desktop) return FALSE;
5673 if (!g_object_get_data (tbl, "esc-pressed")) {
5674 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5675 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5676 sp_text_toolbox_size_changed (cbox, tbl);
5677 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5678 }
5679 return FALSE; // I consumed the event
5680 }
5683 gboolean
5684 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5685 {
5686 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5687 if (!desktop) return FALSE;
5689 switch (get_group0_keyval (event)) {
5690 case GDK_Escape: // defocus
5691 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5692 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5693 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5694 return TRUE; // I consumed the event
5695 break;
5696 case GDK_Return: // defocus
5697 case GDK_KP_Enter:
5698 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5699 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5700 sp_text_toolbox_size_changed (cbox, tbl);
5701 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5702 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5703 return TRUE; // I consumed the event
5704 break;
5705 }
5706 return FALSE;
5707 }
5709 void
5710 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5711 GObject *tbl)
5712 {
5713 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5714 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5715 int x, y;
5717 if (!popdown_visible)
5718 {
5719 gdk_window_get_origin (widget->window, &x, &y);
5720 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5721 gtk_widget_show_all (popdown);
5722 //sp_transientize (popdown);
5724 gdk_pointer_grab (widget->window, TRUE,
5725 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5726 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5727 GDK_POINTER_MOTION_MASK),
5728 NULL, NULL, GDK_CURRENT_TIME);
5730 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5732 popdown_visible = true;
5733 }
5734 else
5735 {
5736 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5737 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5738 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5739 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5740 gtk_widget_hide (popdown);
5741 popdown_visible = false;
5742 }
5743 }
5745 gboolean
5746 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5747 GdkEventFocus */*event*/,
5748 GObject */*tbl*/)
5749 {
5750 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5751 return FALSE;
5752 }
5754 gboolean
5755 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5756 GdkEventFocus */*event*/,
5757 GObject */*tbl*/)
5758 {
5759 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5761 if (popdown_hasfocus) {
5762 gtk_widget_hide (popdown);
5763 popdown_hasfocus = false;
5764 popdown_visible = false;
5765 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5766 return TRUE;
5767 }
5768 return FALSE;
5769 }
5771 gboolean
5772 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5773 GdkEventFocus */*event*/,
5774 GObject */*tbl*/)
5775 {
5776 popdown_hasfocus = true;
5777 return TRUE;
5778 }
5781 void
5782 cell_data_func (GtkTreeViewColumn */*column*/,
5783 GtkCellRenderer *cell,
5784 GtkTreeModel *tree_model,
5785 GtkTreeIter *iter,
5786 gpointer /*data*/)
5787 {
5788 gchar *family;
5789 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5790 gchar *const family_escaped = g_markup_escape_text(family, -1);
5792 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5793 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5795 std::stringstream markup;
5796 markup << family_escaped << " <span foreground='darkgray' font_family='"
5797 << family_escaped << "'>" << sample_escaped << "</span>";
5798 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5800 g_free(family);
5801 g_free(family_escaped);
5802 g_free(sample_escaped);
5803 }
5805 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5806 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5807 if (completion) {
5808 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5809 g_object_unref (completion);
5810 }
5811 }
5813 GtkWidget*
5814 sp_text_toolbox_new (SPDesktop *desktop)
5815 {
5816 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5817 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5819 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5820 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5822 GtkTooltips *tt = gtk_tooltips_new();
5823 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5825 ////////////Family
5826 //Window
5827 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5828 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5830 //Entry
5831 GtkWidget *entry = gtk_entry_new ();
5832 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5833 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5834 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5835 gtk_entry_completion_set_text_column (completion, 0);
5836 gtk_entry_completion_set_minimum_key_length (completion, 1);
5837 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5838 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5839 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5840 gtk_toolbar_append_widget( tbl, entry, "", "" );
5841 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5843 //Button
5844 GtkWidget *button = gtk_button_new ();
5845 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5846 gtk_toolbar_append_widget( tbl, button, "", "");
5848 //Popdown
5849 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5850 GtkWidget *treeview = gtk_tree_view_new ();
5852 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5853 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5854 gtk_tree_view_column_pack_start (column, cell, FALSE);
5855 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5856 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5857 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5859 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5860 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5861 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5863 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5865 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5866 gtk_container_add (GTK_CONTAINER (sw), treeview);
5868 gtk_container_add (GTK_CONTAINER (window), sw);
5869 gtk_widget_set_size_request (window, 300, 450);
5871 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5872 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5873 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5875 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5877 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5878 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5879 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5881 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5882 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5884 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5885 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5886 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5887 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5888 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5890 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5891 GtkWidget *box = gtk_event_box_new ();
5892 gtk_container_add (GTK_CONTAINER (box), image);
5893 gtk_toolbar_append_widget( tbl, box, "", "");
5894 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5895 GtkTooltips *tooltips = gtk_tooltips_new ();
5896 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5897 gtk_widget_hide (GTK_WIDGET (box));
5898 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5900 ////////////Size
5901 gchar const *const sizes[] = {
5902 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5903 "16", "18", "20", "22", "24", "28",
5904 "32", "36", "40", "48", "56", "64", "72", "144"
5905 };
5907 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5908 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5909 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5910 }
5911 gtk_widget_set_size_request (cbox, 80, -1);
5912 gtk_toolbar_append_widget( tbl, cbox, "", "");
5913 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5914 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5915 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5916 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5918 ////////////Text anchor
5919 GtkWidget *group = gtk_radio_button_new (NULL);
5920 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5921 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5923 // left
5924 GtkWidget *rbutton = group;
5925 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5926 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5927 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5929 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5930 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5931 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5932 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5934 // center
5935 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5936 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5937 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5938 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5940 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5941 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5942 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5943 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5945 // right
5946 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5947 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5948 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5949 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5951 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5952 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5953 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5954 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5956 // fill
5957 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5958 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5959 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5960 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5962 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5963 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5964 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5965 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5967 gtk_toolbar_append_widget( tbl, row, "", "");
5969 //spacer
5970 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5972 ////////////Text style
5973 row = gtk_hbox_new (FALSE, 4);
5975 // bold
5976 rbutton = gtk_toggle_button_new ();
5977 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5978 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5979 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5980 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5982 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5983 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5984 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5986 // italic
5987 rbutton = gtk_toggle_button_new ();
5988 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5989 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5990 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5991 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5993 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5994 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5995 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5997 gtk_toolbar_append_widget( tbl, row, "", "");
5999 //spacer
6000 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6002 ////////////Text orientation
6003 group = gtk_radio_button_new (NULL);
6004 row = gtk_hbox_new (FALSE, 4);
6005 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6007 // horizontal
6008 rbutton = group;
6009 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6010 gtk_container_add (GTK_CONTAINER (rbutton),
6011 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6012 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6013 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6015 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6016 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6017 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6019 // vertical
6020 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6021 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6022 gtk_container_add (GTK_CONTAINER (rbutton),
6023 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6024 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6025 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6027 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6028 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6029 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6030 gtk_toolbar_append_widget( tbl, row, "", "" );
6033 //watch selection
6034 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6036 sigc::connection *c_selection_changed =
6037 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6038 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6039 pool->add_connection ("selection-changed", c_selection_changed);
6041 sigc::connection *c_selection_modified =
6042 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6043 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6044 pool->add_connection ("selection-modified", c_selection_modified);
6046 sigc::connection *c_subselection_changed =
6047 new sigc::connection (desktop->connectToolSubselectionChanged
6048 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6049 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6051 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6054 gtk_widget_show_all( GTK_WIDGET(tbl) );
6056 return GTK_WIDGET(tbl);
6057 } // end of sp_text_toolbox_new()
6059 }//<unnamed> namespace
6062 //#########################
6063 //## Connector ##
6064 //#########################
6066 static void sp_connector_path_set_avoid(void)
6067 {
6068 cc_selection_set_avoid(true);
6069 }
6072 static void sp_connector_path_set_ignore(void)
6073 {
6074 cc_selection_set_avoid(false);
6075 }
6079 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6080 {
6081 // quit if run by the _changed callbacks
6082 if (g_object_get_data( tbl, "freeze" )) {
6083 return;
6084 }
6086 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6087 SPDocument *doc = sp_desktop_document(desktop);
6089 if (!sp_document_get_undo_sensitive(doc))
6090 {
6091 return;
6092 }
6094 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6096 if ( repr->attribute("inkscape:connector-spacing") ) {
6097 gdouble priorValue = gtk_adjustment_get_value(adj);
6098 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6099 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6100 return;
6101 }
6102 } else if ( adj->value == defaultConnSpacing ) {
6103 return;
6104 }
6106 // in turn, prevent callbacks from responding
6107 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6109 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6110 SP_OBJECT(desktop->namedview)->updateRepr();
6112 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6113 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6114 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6115 NR::Matrix m = NR::identity();
6116 avoid_item_move(&m, item);
6117 }
6119 if (items) {
6120 g_slist_free(items);
6121 }
6123 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6124 _("Change connector spacing"));
6126 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6128 spinbutton_defocus(GTK_OBJECT(tbl));
6129 }
6131 static void sp_connector_graph_layout(void)
6132 {
6133 if (!SP_ACTIVE_DESKTOP) return;
6135 // hack for clones, see comment in align-and-distribute.cpp
6136 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6137 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6139 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6141 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6143 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6144 }
6146 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6147 {
6148 if ( gtk_toggle_action_get_active( act ) ) {
6149 prefs_set_string_attribute("tools.connector", "directedlayout",
6150 "true");
6151 } else {
6152 prefs_set_string_attribute("tools.connector", "directedlayout",
6153 "false");
6154 }
6155 }
6157 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6158 {
6159 if ( gtk_toggle_action_get_active( act ) ) {
6160 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6161 "true");
6162 } else {
6163 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6164 "false");
6165 }
6166 }
6169 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6170 {
6171 prefs_set_double_attribute("tools.connector", "length", adj->value);
6172 spinbutton_defocus(GTK_OBJECT(tbl));
6173 }
6175 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6176 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6177 bool /*is_interactive*/, gpointer data)
6178 {
6179 GtkWidget *tbl = GTK_WIDGET(data);
6181 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6182 return;
6183 }
6184 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6185 return;
6186 }
6188 GtkAdjustment *adj = (GtkAdjustment*)
6189 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6190 gdouble spacing = defaultConnSpacing;
6191 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6193 gtk_adjustment_set_value(adj, spacing);
6194 }
6197 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6198 NULL, /* child_added */
6199 NULL, /* child_removed */
6200 connector_tb_event_attr_changed,
6201 NULL, /* content_changed */
6202 NULL /* order_changed */
6203 };
6206 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6207 {
6208 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6210 {
6211 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6212 _("Avoid"),
6213 _("Make connectors avoid selected objects"),
6214 "connector_avoid",
6215 secondarySize );
6216 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6217 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6218 }
6220 {
6221 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6222 _("Ignore"),
6223 _("Make connectors ignore selected objects"),
6224 "connector_ignore",
6225 secondarySize );
6226 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6227 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6228 }
6230 EgeAdjustmentAction* eact = 0;
6232 // Spacing spinbox
6233 eact = create_adjustment_action( "ConnectorSpacingAction",
6234 _("Connector Spacing"), _("Spacing:"),
6235 _("The amount of space left around objects by auto-routing connectors"),
6236 "tools.connector", "spacing", defaultConnSpacing,
6237 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6238 0, 100, 1.0, 10.0,
6239 0, 0, 0,
6240 connector_spacing_changed, 1, 0 );
6241 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6243 // Graph (connector network) layout
6244 {
6245 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6246 _("Graph"),
6247 _("Nicely arrange selected connector network"),
6248 "graph_layout",
6249 secondarySize );
6250 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6251 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6252 }
6254 // Default connector length spinbox
6255 eact = create_adjustment_action( "ConnectorLengthAction",
6256 _("Connector Length"), _("Length:"),
6257 _("Ideal length for connectors when layout is applied"),
6258 "tools.connector", "length", 100,
6259 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6260 10, 1000, 10.0, 100.0,
6261 0, 0, 0,
6262 connector_length_changed, 1, 0 );
6263 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6266 // Directed edges toggle button
6267 {
6268 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6269 _("Downwards"),
6270 _("Make connectors with end-markers (arrows) point downwards"),
6271 "directed_graph",
6272 Inkscape::ICON_SIZE_DECORATION );
6273 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6275 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6276 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6277 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6279 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6280 }
6282 // Avoid overlaps toggle button
6283 {
6284 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6285 _("Remove overlaps"),
6286 _("Do not allow overlapping shapes"),
6287 "remove_overlaps",
6288 Inkscape::ICON_SIZE_DECORATION );
6289 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6291 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6292 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6293 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6295 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6296 }
6298 // Code to watch for changes to the connector-spacing attribute in
6299 // the XML.
6300 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6301 g_assert(repr != NULL);
6303 purge_repr_listener( holder, holder );
6305 if (repr) {
6306 g_object_set_data( holder, "repr", repr );
6307 Inkscape::GC::anchor(repr);
6308 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6309 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6310 }
6311 } // end of sp_connector_toolbox_prep()
6314 //#########################
6315 //## Paintbucket ##
6316 //#########################
6318 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6319 {
6320 gint channels = ege_select_one_action_get_active( act );
6321 flood_channels_set_channels( channels );
6322 }
6324 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6325 {
6326 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6327 }
6329 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6330 {
6331 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6332 }
6334 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6335 {
6336 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6337 SPUnit const *unit = tracker->getActiveUnit();
6339 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6341 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6342 }
6344 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6345 {
6346 // FIXME: make defaults settable via Inkscape Options
6347 struct KeyValue {
6348 char const *key;
6349 double value;
6350 } const key_values[] = {
6351 {"threshold", 15},
6352 {"offset", 0.0}
6353 };
6355 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6356 KeyValue const &kv = key_values[i];
6357 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6358 if ( adj ) {
6359 gtk_adjustment_set_value(adj, kv.value);
6360 }
6361 }
6363 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6364 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6365 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6366 ege_select_one_action_set_active( autogap_action, 0 );
6367 }
6369 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6370 {
6371 EgeAdjustmentAction* eact = 0;
6373 {
6374 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6376 GList* items = 0;
6377 gint count = 0;
6378 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6379 {
6380 GtkTreeIter iter;
6381 gtk_list_store_append( model, &iter );
6382 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6383 count++;
6384 }
6385 g_list_free( items );
6386 items = 0;
6387 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6388 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6389 ege_select_one_action_set_appearance( act1, "compact" );
6390 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6391 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6392 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6393 g_object_set_data( holder, "channels_action", act1 );
6394 }
6396 // Spacing spinbox
6397 {
6398 eact = create_adjustment_action(
6399 "ThresholdAction",
6400 _("Fill Threshold"), _("Threshold:"),
6401 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6402 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6403 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6404 0, 0, 0,
6405 paintbucket_threshold_changed, 1, 0 );
6407 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6408 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6409 }
6411 // Create the units menu.
6412 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6413 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6414 if (stored_unit)
6415 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6416 g_object_set_data( holder, "tracker", tracker );
6417 {
6418 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6419 gtk_action_group_add_action( mainActions, act );
6420 }
6422 // Offset spinbox
6423 {
6424 eact = create_adjustment_action(
6425 "OffsetAction",
6426 _("Grow/shrink by"), _("Grow/shrink by:"),
6427 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6428 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6429 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6430 0, 0, 0,
6431 paintbucket_offset_changed, 1, 2);
6432 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6434 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6435 }
6437 /* Auto Gap */
6438 {
6439 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6441 GList* items = 0;
6442 gint count = 0;
6443 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6444 {
6445 GtkTreeIter iter;
6446 gtk_list_store_append( model, &iter );
6447 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6448 count++;
6449 }
6450 g_list_free( items );
6451 items = 0;
6452 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6453 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6454 ege_select_one_action_set_appearance( act2, "compact" );
6455 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6456 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6457 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6458 g_object_set_data( holder, "autogap_action", act2 );
6459 }
6461 /* Reset */
6462 {
6463 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6464 _("Defaults"),
6465 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6466 GTK_STOCK_CLEAR );
6467 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6468 gtk_action_group_add_action( mainActions, act );
6469 gtk_action_set_sensitive( act, TRUE );
6470 }
6472 }
6474 /*
6475 Local Variables:
6476 mode:c++
6477 c-file-style:"stroustrup"
6478 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6479 indent-tabs-mode:nil
6480 fill-column:99
6481 End:
6482 */
6483 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :