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_node_toolbox_sel_changed()\n");
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 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4859 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4860 if (lpe && lpe->effectType() == ANGLE_BISECTOR) {
4861 LPEAngleBisector *lpeab = dynamic_cast<LPEAngleBisector*>(lpe);
4862 g_object_set_data(tbl, "currentlpe", lpeab);
4863 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4864 gtk_action_set_sensitive(w, TRUE);
4865 } else {
4866 g_object_set_data(tbl, "currentlpe", NULL);
4867 g_object_set_data(tbl, "currentlpeitem", NULL);
4868 gtk_action_set_sensitive(w, FALSE);
4869 }
4870 } else {
4871 g_object_set_data(tbl, "currentlpe", NULL);
4872 g_object_set_data(tbl, "currentlpeitem", NULL);
4873 gtk_action_set_sensitive(w, FALSE);
4874 }
4875 }
4876 }
4878 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4879 {
4880 /** Automatically create a list of LPEs that get added to the toolbar **/
4881 {
4882 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4884 GtkTreeIter iter;
4886 Inkscape::LivePathEffect::EffectType type;
4887 for (int i = 0; i < num_subtools; ++i) {
4888 type = lpesubtools[i];
4889 gtk_list_store_append( model, &iter );
4890 gtk_list_store_set( model, &iter,
4891 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4892 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4893 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4894 -1 );
4895 }
4897 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4898 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4899 g_object_set_data( holder, "lpetool_mode_action", act );
4901 ege_select_one_action_set_appearance( act, "full" );
4902 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4903 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4904 ege_select_one_action_set_icon_column( act, 2 );
4905 ege_select_one_action_set_tooltip_column( act, 1 );
4907 gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4908 ege_select_one_action_set_active( act, lpeToolMode );
4909 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4910 }
4912 /* Test action */
4913 /**
4914 {
4915 EgeAdjustmentAction* eact = 0;
4916 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
4917 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
4918 eact = create_adjustment_action( "TestLPEAction",
4919 _("Test value"), _("Test value:"), _("Test for interactive control widgets ..."),
4920 "tools.lpetool", "testvalue", 0,
4921 GTK_WIDGET(desktop->canvas), NULL us, holder, TRUE, "altx-lpetool",
4922 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
4923 labels, values, G_N_ELEMENTS(labels),
4924 sp_lpetool_test_value_changed );
4925 //tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4926 g_object_set_data( holder, "lpetool_test_action", eact );
4927 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
4928 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4929 }
4930 **/
4932 //watch selection
4933 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
4935 sigc::connection *c_selection_changed =
4936 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4937 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
4938 pool->add_connection ("selection-changed", c_selection_changed);
4939 }
4941 //########################
4942 //## Eraser ##
4943 //########################
4945 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4946 {
4947 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4948 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4949 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4950 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4951 }
4953 // only take action if run by the attr_changed listener
4954 if (!g_object_get_data( tbl, "freeze" )) {
4955 // in turn, prevent listener from responding
4956 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4958 if ( eraserMode != 0 ) {
4959 } else {
4960 }
4961 // TODO finish implementation
4963 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4964 }
4965 }
4967 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4968 {
4969 {
4970 /* Width */
4971 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4972 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4973 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4974 _("Pen Width"), _("Width:"),
4975 _("The width of the eraser pen (relative to the visible canvas area)"),
4976 "tools.eraser", "width", 15,
4977 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4978 1, 100, 1.0, 0.0,
4979 labels, values, G_N_ELEMENTS(labels),
4980 sp_ddc_width_value_changed, 0.01, 0, 100 );
4981 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4982 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4983 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4984 }
4986 {
4987 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4989 GtkTreeIter iter;
4990 gtk_list_store_append( model, &iter );
4991 gtk_list_store_set( model, &iter,
4992 0, _("Delete"),
4993 1, _("Delete objects touched by the eraser"),
4994 2, "delete_object",
4995 -1 );
4997 gtk_list_store_append( model, &iter );
4998 gtk_list_store_set( model, &iter,
4999 0, _("Cut"),
5000 1, _("Cut out from objects"),
5001 2, "difference",
5002 -1 );
5004 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5005 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5006 g_object_set_data( holder, "eraser_mode_action", act );
5008 ege_select_one_action_set_appearance( act, "full" );
5009 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5010 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5011 ege_select_one_action_set_icon_column( act, 2 );
5012 ege_select_one_action_set_tooltip_column( act, 1 );
5014 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5015 ege_select_one_action_set_active( act, eraserMode );
5016 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5017 }
5019 }
5021 //########################
5022 //## Text Toolbox ##
5023 //########################
5024 /*
5025 static void
5026 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5027 {
5028 //Call back for letter sizing spinbutton
5029 }
5031 static void
5032 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5033 {
5034 //Call back for line height spinbutton
5035 }
5037 static void
5038 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5039 {
5040 //Call back for horizontal kerning spinbutton
5041 }
5043 static void
5044 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5045 {
5046 //Call back for vertical kerning spinbutton
5047 }
5049 static void
5050 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5051 {
5052 //Call back for letter rotation spinbutton
5053 }*/
5055 namespace {
5057 bool popdown_visible = false;
5058 bool popdown_hasfocus = false;
5060 void
5061 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5062 {
5063 SPStyle *query =
5064 sp_style_new (SP_ACTIVE_DOCUMENT);
5066 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5068 int result_family =
5069 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5071 int result_style =
5072 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5074 int result_numbers =
5075 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5077 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5079 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5080 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5081 // there are no texts in selection, read from prefs
5083 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5084 if (repr) {
5085 sp_style_read_from_repr (query, repr);
5086 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5087 // do not reset the toolbar style from prefs if we already did it last time
5088 sp_style_unref(query);
5089 return;
5090 }
5091 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5092 } else {
5093 sp_style_unref(query);
5094 return;
5095 }
5096 } else {
5097 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5098 }
5100 if (query->text)
5101 {
5102 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5103 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5104 gtk_entry_set_text (GTK_ENTRY (entry), "");
5106 } else if (query->text->font_specification.value || query->text->font_family.value) {
5108 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5110 // Get the font that corresponds
5111 Glib::ustring familyName;
5113 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5114 if (font) {
5115 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5116 font->Unref();
5117 font = NULL;
5118 }
5120 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5122 Gtk::TreePath path;
5123 try {
5124 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5125 } catch (...) {
5126 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5127 sp_style_unref(query);
5128 return;
5129 }
5131 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5132 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5134 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5136 gtk_tree_selection_select_path (tselection, path.gobj());
5137 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5139 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5140 }
5142 //Size
5143 {
5144 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5145 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5146 g_object_set_data(tbl, "size-block", gpointer(1));
5147 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5148 g_object_set_data(tbl, "size-block", gpointer(0));
5149 g_free(str);
5150 }
5152 //Anchor
5153 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5154 {
5155 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5156 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5157 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5158 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5159 }
5160 else
5161 {
5162 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5163 {
5164 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5165 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5166 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5167 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5168 }
5169 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5170 {
5171 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5172 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5173 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5174 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5175 }
5176 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5177 {
5178 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5179 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5180 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5181 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5182 }
5183 }
5185 //Style
5186 {
5187 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5189 gboolean active = gtk_toggle_button_get_active (button);
5190 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5192 if (active != check)
5193 {
5194 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5195 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5196 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5197 }
5198 }
5200 {
5201 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5203 gboolean active = gtk_toggle_button_get_active (button);
5204 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5206 if (active != check)
5207 {
5208 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5209 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5210 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5211 }
5212 }
5214 //Orientation
5215 //locking both buttons, changing one affect all group (both)
5216 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5217 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5219 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5220 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5222 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5223 {
5224 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5225 }
5226 else
5227 {
5228 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5229 }
5230 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5231 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5232 }
5234 sp_style_unref(query);
5235 }
5237 void
5238 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5239 {
5240 sp_text_toolbox_selection_changed (selection, tbl);
5241 }
5243 void
5244 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5245 {
5246 sp_text_toolbox_selection_changed (NULL, tbl);
5247 }
5249 void
5250 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5251 GObject *tbl)
5252 {
5253 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5254 GtkTreeModel *model = 0;
5255 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5256 GtkTreeIter iter;
5257 char *family = 0;
5259 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5260 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5262 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5263 return;
5264 }
5266 gtk_tree_model_get (model, &iter, 0, &family, -1);
5268 if (g_object_get_data (G_OBJECT (selection), "block"))
5269 {
5270 gtk_entry_set_text (GTK_ENTRY (entry), family);
5271 return;
5272 }
5274 gtk_entry_set_text (GTK_ENTRY (entry), family);
5276 SPStyle *query =
5277 sp_style_new (SP_ACTIVE_DOCUMENT);
5279 int result_fontspec =
5280 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5282 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5284 SPCSSAttr *css = sp_repr_css_attr_new ();
5287 // First try to get the font spec from the stored value
5288 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5290 if (fontSpec.empty()) {
5291 // Construct a new font specification if it does not yet exist
5292 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5293 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5294 fontFromStyle->Unref();
5295 }
5297 if (!fontSpec.empty()) {
5298 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5299 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5300 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5301 if (font) {
5302 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5304 // Set all the these just in case they were altered when finding the best
5305 // match for the new family and old style...
5307 gchar c[256];
5309 font->Family(c, 256);
5310 sp_repr_css_set_property (css, "font-family", c);
5312 font->Attribute( "weight", c, 256);
5313 sp_repr_css_set_property (css, "font-weight", c);
5315 font->Attribute("style", c, 256);
5316 sp_repr_css_set_property (css, "font-style", c);
5318 font->Attribute("stretch", c, 256);
5319 sp_repr_css_set_property (css, "font-stretch", c);
5321 font->Attribute("variant", c, 256);
5322 sp_repr_css_set_property (css, "font-variant", c);
5324 font->Unref();
5325 }
5326 }
5327 }
5329 // If querying returned nothing, set the default style of the tool (for new texts)
5330 if (result_fontspec == QUERY_STYLE_NOTHING)
5331 {
5332 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5333 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5334 }
5335 else
5336 {
5337 sp_desktop_set_style (desktop, css, true, true);
5338 }
5340 sp_style_unref(query);
5342 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5343 _("Text: Change font family"));
5344 sp_repr_css_attr_unref (css);
5345 g_free(family);
5346 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5348 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5349 }
5351 /* This is where execution comes when the contents of the font family box have been completed
5352 by the press of the return key */
5353 void
5354 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5355 GObject *tbl)
5356 {
5357 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5359 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5360 try {
5361 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5362 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5363 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5364 gtk_tree_selection_select_path (selection, path.gobj());
5365 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5366 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5367 } catch (...) {
5368 if (family && strlen (family))
5369 {
5370 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5371 }
5372 }
5373 }
5375 void
5376 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5377 gpointer data)
5378 {
5379 if (g_object_get_data (G_OBJECT (button), "block")) return;
5380 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5381 int prop = GPOINTER_TO_INT(data);
5383 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5384 SPCSSAttr *css = sp_repr_css_attr_new ();
5386 switch (prop)
5387 {
5388 case 0:
5389 {
5390 sp_repr_css_set_property (css, "text-anchor", "start");
5391 sp_repr_css_set_property (css, "text-align", "start");
5392 break;
5393 }
5394 case 1:
5395 {
5396 sp_repr_css_set_property (css, "text-anchor", "middle");
5397 sp_repr_css_set_property (css, "text-align", "center");
5398 break;
5399 }
5401 case 2:
5402 {
5403 sp_repr_css_set_property (css, "text-anchor", "end");
5404 sp_repr_css_set_property (css, "text-align", "end");
5405 break;
5406 }
5408 case 3:
5409 {
5410 sp_repr_css_set_property (css, "text-anchor", "start");
5411 sp_repr_css_set_property (css, "text-align", "justify");
5412 break;
5413 }
5414 }
5416 SPStyle *query =
5417 sp_style_new (SP_ACTIVE_DOCUMENT);
5418 int result_numbers =
5419 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5421 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5422 if (result_numbers == QUERY_STYLE_NOTHING)
5423 {
5424 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5425 }
5427 sp_style_unref(query);
5429 sp_desktop_set_style (desktop, css, true, true);
5430 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5431 _("Text: Change alignment"));
5432 sp_repr_css_attr_unref (css);
5434 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5435 }
5437 void
5438 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5439 gpointer data)
5440 {
5441 if (g_object_get_data (G_OBJECT (button), "block")) return;
5443 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5444 SPCSSAttr *css = sp_repr_css_attr_new ();
5445 int prop = GPOINTER_TO_INT(data);
5446 bool active = gtk_toggle_button_get_active (button);
5448 SPStyle *query =
5449 sp_style_new (SP_ACTIVE_DOCUMENT);
5451 int result_fontspec =
5452 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5454 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5455 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5456 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5458 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5459 Glib::ustring newFontSpec = "";
5461 if (fontSpec.empty()) {
5462 // Construct a new font specification if it does not yet exist
5463 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5464 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5465 fontFromStyle->Unref();
5466 }
5468 switch (prop)
5469 {
5470 case 0:
5471 {
5472 if (!fontSpec.empty()) {
5473 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5474 }
5475 if (fontSpec != newFontSpec) {
5476 // Don't even set the bold if the font didn't exist on the system
5477 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5478 }
5479 break;
5480 }
5482 case 1:
5483 {
5484 if (!fontSpec.empty()) {
5485 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5486 }
5487 if (fontSpec != newFontSpec) {
5488 // Don't even set the italic if the font didn't exist on the system
5489 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5490 }
5491 break;
5492 }
5493 }
5495 if (!newFontSpec.empty()) {
5496 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5497 }
5499 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5500 if (result_fontspec == QUERY_STYLE_NOTHING)
5501 {
5502 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5503 }
5505 sp_style_unref(query);
5507 sp_desktop_set_style (desktop, css, true, true);
5508 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5509 _("Text: Change font style"));
5510 sp_repr_css_attr_unref (css);
5512 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5513 }
5515 void
5516 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5517 gpointer data)
5518 {
5519 if (g_object_get_data (G_OBJECT (button), "block")) {
5520 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5521 return;
5522 }
5524 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5525 SPCSSAttr *css = sp_repr_css_attr_new ();
5526 int prop = GPOINTER_TO_INT(data);
5528 switch (prop)
5529 {
5530 case 0:
5531 {
5532 sp_repr_css_set_property (css, "writing-mode", "lr");
5533 break;
5534 }
5536 case 1:
5537 {
5538 sp_repr_css_set_property (css, "writing-mode", "tb");
5539 break;
5540 }
5541 }
5543 SPStyle *query =
5544 sp_style_new (SP_ACTIVE_DOCUMENT);
5545 int result_numbers =
5546 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5548 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5549 if (result_numbers == QUERY_STYLE_NOTHING)
5550 {
5551 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5552 }
5554 sp_desktop_set_style (desktop, css, true, true);
5555 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5556 _("Text: Change orientation"));
5557 sp_repr_css_attr_unref (css);
5559 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5560 }
5562 gboolean
5563 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5564 {
5565 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5566 if (!desktop) return FALSE;
5568 switch (get_group0_keyval (event)) {
5569 case GDK_Escape: // defocus
5570 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5571 sp_text_toolbox_selection_changed (NULL, tbl); // update
5572 return TRUE; // I consumed the event
5573 break;
5574 }
5575 return FALSE;
5576 }
5578 gboolean
5579 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5580 {
5581 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5582 if (!desktop) return FALSE;
5584 switch (get_group0_keyval (event)) {
5585 case GDK_KP_Enter:
5586 case GDK_Return:
5587 case GDK_Escape: // defocus
5588 gtk_widget_hide (w);
5589 popdown_visible = false;
5590 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5591 return TRUE; // I consumed the event
5592 break;
5593 case GDK_w:
5594 case GDK_W:
5595 if (event->state & GDK_CONTROL_MASK) {
5596 gtk_widget_hide (w);
5597 popdown_visible = false;
5598 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5599 return TRUE; // I consumed the event
5600 }
5601 break;
5602 }
5603 return FALSE;
5604 }
5607 void
5608 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5609 GObject *tbl)
5610 {
5611 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5613 if (g_object_get_data (tbl, "size-block")) return;
5615 // If this is not from selecting a size in the list (in which case get_active will give the
5616 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5617 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5618 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5619 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5620 return;
5622 gdouble value = -1;
5623 {
5624 gchar *endptr;
5625 gchar *const text = gtk_combo_box_get_active_text(cbox);
5626 if (text) {
5627 value = g_strtod(text, &endptr);
5628 if (endptr == text) { // Conversion failed, non-numeric input.
5629 value = -1;
5630 }
5631 g_free(text);
5632 }
5633 }
5634 if (value <= 0) {
5635 return; // could not parse value
5636 }
5638 SPCSSAttr *css = sp_repr_css_attr_new ();
5639 Inkscape::CSSOStringStream osfs;
5640 osfs << value;
5641 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5643 SPStyle *query =
5644 sp_style_new (SP_ACTIVE_DOCUMENT);
5645 int result_numbers =
5646 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5648 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5649 if (result_numbers == QUERY_STYLE_NOTHING)
5650 {
5651 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5652 }
5654 sp_style_unref(query);
5656 sp_desktop_set_style (desktop, css, true, true);
5657 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5658 _("Text: Change font size"));
5659 sp_repr_css_attr_unref (css);
5661 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5662 }
5664 gboolean
5665 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5666 {
5667 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5668 if (!desktop) return FALSE;
5670 if (!g_object_get_data (tbl, "esc-pressed")) {
5671 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5672 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5673 sp_text_toolbox_size_changed (cbox, tbl);
5674 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5675 }
5676 return FALSE; // I consumed the event
5677 }
5680 gboolean
5681 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5682 {
5683 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5684 if (!desktop) return FALSE;
5686 switch (get_group0_keyval (event)) {
5687 case GDK_Escape: // defocus
5688 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5689 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5690 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5691 return TRUE; // I consumed the event
5692 break;
5693 case GDK_Return: // defocus
5694 case GDK_KP_Enter:
5695 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5696 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5697 sp_text_toolbox_size_changed (cbox, tbl);
5698 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5699 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5700 return TRUE; // I consumed the event
5701 break;
5702 }
5703 return FALSE;
5704 }
5706 void
5707 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5708 GObject *tbl)
5709 {
5710 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5711 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5712 int x, y;
5714 if (!popdown_visible)
5715 {
5716 gdk_window_get_origin (widget->window, &x, &y);
5717 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5718 gtk_widget_show_all (popdown);
5719 //sp_transientize (popdown);
5721 gdk_pointer_grab (widget->window, TRUE,
5722 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5723 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5724 GDK_POINTER_MOTION_MASK),
5725 NULL, NULL, GDK_CURRENT_TIME);
5727 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5729 popdown_visible = true;
5730 }
5731 else
5732 {
5733 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5734 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5735 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5736 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5737 gtk_widget_hide (popdown);
5738 popdown_visible = false;
5739 }
5740 }
5742 gboolean
5743 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5744 GdkEventFocus */*event*/,
5745 GObject */*tbl*/)
5746 {
5747 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5748 return FALSE;
5749 }
5751 gboolean
5752 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5753 GdkEventFocus */*event*/,
5754 GObject */*tbl*/)
5755 {
5756 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5758 if (popdown_hasfocus) {
5759 gtk_widget_hide (popdown);
5760 popdown_hasfocus = false;
5761 popdown_visible = false;
5762 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5763 return TRUE;
5764 }
5765 return FALSE;
5766 }
5768 gboolean
5769 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5770 GdkEventFocus */*event*/,
5771 GObject */*tbl*/)
5772 {
5773 popdown_hasfocus = true;
5774 return TRUE;
5775 }
5778 void
5779 cell_data_func (GtkTreeViewColumn */*column*/,
5780 GtkCellRenderer *cell,
5781 GtkTreeModel *tree_model,
5782 GtkTreeIter *iter,
5783 gpointer /*data*/)
5784 {
5785 gchar *family;
5786 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5787 gchar *const family_escaped = g_markup_escape_text(family, -1);
5789 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5790 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5792 std::stringstream markup;
5793 markup << family_escaped << " <span foreground='darkgray' font_family='"
5794 << family_escaped << "'>" << sample_escaped << "</span>";
5795 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5797 g_free(family);
5798 g_free(family_escaped);
5799 g_free(sample_escaped);
5800 }
5802 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5803 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5804 if (completion) {
5805 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5806 g_object_unref (completion);
5807 }
5808 }
5810 GtkWidget*
5811 sp_text_toolbox_new (SPDesktop *desktop)
5812 {
5813 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5814 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5816 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5817 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5819 GtkTooltips *tt = gtk_tooltips_new();
5820 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5822 ////////////Family
5823 //Window
5824 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5825 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5827 //Entry
5828 GtkWidget *entry = gtk_entry_new ();
5829 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5830 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5831 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5832 gtk_entry_completion_set_text_column (completion, 0);
5833 gtk_entry_completion_set_minimum_key_length (completion, 1);
5834 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5835 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5836 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5837 gtk_toolbar_append_widget( tbl, entry, "", "" );
5838 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5840 //Button
5841 GtkWidget *button = gtk_button_new ();
5842 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5843 gtk_toolbar_append_widget( tbl, button, "", "");
5845 //Popdown
5846 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5847 GtkWidget *treeview = gtk_tree_view_new ();
5849 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5850 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5851 gtk_tree_view_column_pack_start (column, cell, FALSE);
5852 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5853 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5854 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5856 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5857 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5858 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5860 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5862 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5863 gtk_container_add (GTK_CONTAINER (sw), treeview);
5865 gtk_container_add (GTK_CONTAINER (window), sw);
5866 gtk_widget_set_size_request (window, 300, 450);
5868 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5869 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5870 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5872 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5874 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5875 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5876 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5878 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5879 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5881 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5882 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5883 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5884 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5885 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5887 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5888 GtkWidget *box = gtk_event_box_new ();
5889 gtk_container_add (GTK_CONTAINER (box), image);
5890 gtk_toolbar_append_widget( tbl, box, "", "");
5891 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5892 GtkTooltips *tooltips = gtk_tooltips_new ();
5893 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5894 gtk_widget_hide (GTK_WIDGET (box));
5895 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5897 ////////////Size
5898 gchar const *const sizes[] = {
5899 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5900 "16", "18", "20", "22", "24", "28",
5901 "32", "36", "40", "48", "56", "64", "72", "144"
5902 };
5904 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5905 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5906 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5907 }
5908 gtk_widget_set_size_request (cbox, 80, -1);
5909 gtk_toolbar_append_widget( tbl, cbox, "", "");
5910 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5911 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5912 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5913 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5915 ////////////Text anchor
5916 GtkWidget *group = gtk_radio_button_new (NULL);
5917 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5918 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5920 // left
5921 GtkWidget *rbutton = group;
5922 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5923 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5924 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5926 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5927 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5928 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5929 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5931 // center
5932 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5933 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5934 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5935 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5937 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5938 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5939 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5940 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5942 // right
5943 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5944 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5945 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5946 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5948 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5949 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5950 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5951 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5953 // fill
5954 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5955 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5956 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5957 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5959 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5960 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5961 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5962 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5964 gtk_toolbar_append_widget( tbl, row, "", "");
5966 //spacer
5967 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5969 ////////////Text style
5970 row = gtk_hbox_new (FALSE, 4);
5972 // bold
5973 rbutton = gtk_toggle_button_new ();
5974 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5975 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5976 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5977 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5979 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5980 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5981 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5983 // italic
5984 rbutton = gtk_toggle_button_new ();
5985 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5986 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5987 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5988 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5990 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5991 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5992 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5994 gtk_toolbar_append_widget( tbl, row, "", "");
5996 //spacer
5997 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5999 ////////////Text orientation
6000 group = gtk_radio_button_new (NULL);
6001 row = gtk_hbox_new (FALSE, 4);
6002 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6004 // horizontal
6005 rbutton = group;
6006 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6007 gtk_container_add (GTK_CONTAINER (rbutton),
6008 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6009 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6010 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6012 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6013 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6014 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6016 // vertical
6017 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6018 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6019 gtk_container_add (GTK_CONTAINER (rbutton),
6020 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6021 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6022 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6024 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6025 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6026 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6027 gtk_toolbar_append_widget( tbl, row, "", "" );
6030 //watch selection
6031 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6033 sigc::connection *c_selection_changed =
6034 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6035 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6036 pool->add_connection ("selection-changed", c_selection_changed);
6038 sigc::connection *c_selection_modified =
6039 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6040 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6041 pool->add_connection ("selection-modified", c_selection_modified);
6043 sigc::connection *c_subselection_changed =
6044 new sigc::connection (desktop->connectToolSubselectionChanged
6045 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6046 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6048 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6051 gtk_widget_show_all( GTK_WIDGET(tbl) );
6053 return GTK_WIDGET(tbl);
6054 } // end of sp_text_toolbox_new()
6056 }//<unnamed> namespace
6059 //#########################
6060 //## Connector ##
6061 //#########################
6063 static void sp_connector_path_set_avoid(void)
6064 {
6065 cc_selection_set_avoid(true);
6066 }
6069 static void sp_connector_path_set_ignore(void)
6070 {
6071 cc_selection_set_avoid(false);
6072 }
6076 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6077 {
6078 // quit if run by the _changed callbacks
6079 if (g_object_get_data( tbl, "freeze" )) {
6080 return;
6081 }
6083 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6084 SPDocument *doc = sp_desktop_document(desktop);
6086 if (!sp_document_get_undo_sensitive(doc))
6087 {
6088 return;
6089 }
6091 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6093 if ( repr->attribute("inkscape:connector-spacing") ) {
6094 gdouble priorValue = gtk_adjustment_get_value(adj);
6095 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6096 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6097 return;
6098 }
6099 } else if ( adj->value == defaultConnSpacing ) {
6100 return;
6101 }
6103 // in turn, prevent callbacks from responding
6104 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6106 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6107 SP_OBJECT(desktop->namedview)->updateRepr();
6109 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6110 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6111 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6112 NR::Matrix m = NR::identity();
6113 avoid_item_move(&m, item);
6114 }
6116 if (items) {
6117 g_slist_free(items);
6118 }
6120 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6121 _("Change connector spacing"));
6123 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6125 spinbutton_defocus(GTK_OBJECT(tbl));
6126 }
6128 static void sp_connector_graph_layout(void)
6129 {
6130 if (!SP_ACTIVE_DESKTOP) return;
6132 // hack for clones, see comment in align-and-distribute.cpp
6133 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6134 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6136 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6138 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6140 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6141 }
6143 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6144 {
6145 if ( gtk_toggle_action_get_active( act ) ) {
6146 prefs_set_string_attribute("tools.connector", "directedlayout",
6147 "true");
6148 } else {
6149 prefs_set_string_attribute("tools.connector", "directedlayout",
6150 "false");
6151 }
6152 }
6154 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6155 {
6156 if ( gtk_toggle_action_get_active( act ) ) {
6157 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6158 "true");
6159 } else {
6160 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6161 "false");
6162 }
6163 }
6166 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6167 {
6168 prefs_set_double_attribute("tools.connector", "length", adj->value);
6169 spinbutton_defocus(GTK_OBJECT(tbl));
6170 }
6172 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6173 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6174 bool /*is_interactive*/, gpointer data)
6175 {
6176 GtkWidget *tbl = GTK_WIDGET(data);
6178 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6179 return;
6180 }
6181 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6182 return;
6183 }
6185 GtkAdjustment *adj = (GtkAdjustment*)
6186 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6187 gdouble spacing = defaultConnSpacing;
6188 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6190 gtk_adjustment_set_value(adj, spacing);
6191 }
6194 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6195 NULL, /* child_added */
6196 NULL, /* child_removed */
6197 connector_tb_event_attr_changed,
6198 NULL, /* content_changed */
6199 NULL /* order_changed */
6200 };
6203 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6204 {
6205 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6207 {
6208 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6209 _("Avoid"),
6210 _("Make connectors avoid selected objects"),
6211 "connector_avoid",
6212 secondarySize );
6213 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6214 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6215 }
6217 {
6218 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6219 _("Ignore"),
6220 _("Make connectors ignore selected objects"),
6221 "connector_ignore",
6222 secondarySize );
6223 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6224 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6225 }
6227 EgeAdjustmentAction* eact = 0;
6229 // Spacing spinbox
6230 eact = create_adjustment_action( "ConnectorSpacingAction",
6231 _("Connector Spacing"), _("Spacing:"),
6232 _("The amount of space left around objects by auto-routing connectors"),
6233 "tools.connector", "spacing", defaultConnSpacing,
6234 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6235 0, 100, 1.0, 10.0,
6236 0, 0, 0,
6237 connector_spacing_changed, 1, 0 );
6238 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6240 // Graph (connector network) layout
6241 {
6242 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6243 _("Graph"),
6244 _("Nicely arrange selected connector network"),
6245 "graph_layout",
6246 secondarySize );
6247 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6248 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6249 }
6251 // Default connector length spinbox
6252 eact = create_adjustment_action( "ConnectorLengthAction",
6253 _("Connector Length"), _("Length:"),
6254 _("Ideal length for connectors when layout is applied"),
6255 "tools.connector", "length", 100,
6256 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6257 10, 1000, 10.0, 100.0,
6258 0, 0, 0,
6259 connector_length_changed, 1, 0 );
6260 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6263 // Directed edges toggle button
6264 {
6265 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6266 _("Downwards"),
6267 _("Make connectors with end-markers (arrows) point downwards"),
6268 "directed_graph",
6269 Inkscape::ICON_SIZE_DECORATION );
6270 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6272 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6273 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6274 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6276 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6277 }
6279 // Avoid overlaps toggle button
6280 {
6281 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6282 _("Remove overlaps"),
6283 _("Do not allow overlapping shapes"),
6284 "remove_overlaps",
6285 Inkscape::ICON_SIZE_DECORATION );
6286 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6288 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6289 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6290 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6292 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6293 }
6295 // Code to watch for changes to the connector-spacing attribute in
6296 // the XML.
6297 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6298 g_assert(repr != NULL);
6300 purge_repr_listener( holder, holder );
6302 if (repr) {
6303 g_object_set_data( holder, "repr", repr );
6304 Inkscape::GC::anchor(repr);
6305 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6306 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6307 }
6308 } // end of sp_connector_toolbox_prep()
6311 //#########################
6312 //## Paintbucket ##
6313 //#########################
6315 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6316 {
6317 gint channels = ege_select_one_action_get_active( act );
6318 flood_channels_set_channels( channels );
6319 }
6321 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6322 {
6323 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6324 }
6326 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6327 {
6328 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6329 }
6331 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6332 {
6333 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6334 SPUnit const *unit = tracker->getActiveUnit();
6336 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6338 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6339 }
6341 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6342 {
6343 // FIXME: make defaults settable via Inkscape Options
6344 struct KeyValue {
6345 char const *key;
6346 double value;
6347 } const key_values[] = {
6348 {"threshold", 15},
6349 {"offset", 0.0}
6350 };
6352 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6353 KeyValue const &kv = key_values[i];
6354 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6355 if ( adj ) {
6356 gtk_adjustment_set_value(adj, kv.value);
6357 }
6358 }
6360 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6361 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6362 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6363 ege_select_one_action_set_active( autogap_action, 0 );
6364 }
6366 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6367 {
6368 EgeAdjustmentAction* eact = 0;
6370 {
6371 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6373 GList* items = 0;
6374 gint count = 0;
6375 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6376 {
6377 GtkTreeIter iter;
6378 gtk_list_store_append( model, &iter );
6379 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6380 count++;
6381 }
6382 g_list_free( items );
6383 items = 0;
6384 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6385 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6386 ege_select_one_action_set_appearance( act1, "compact" );
6387 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6388 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6389 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6390 g_object_set_data( holder, "channels_action", act1 );
6391 }
6393 // Spacing spinbox
6394 {
6395 eact = create_adjustment_action(
6396 "ThresholdAction",
6397 _("Fill Threshold"), _("Threshold:"),
6398 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6399 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6400 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6401 0, 0, 0,
6402 paintbucket_threshold_changed, 1, 0 );
6404 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6405 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6406 }
6408 // Create the units menu.
6409 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6410 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6411 if (stored_unit)
6412 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6413 g_object_set_data( holder, "tracker", tracker );
6414 {
6415 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6416 gtk_action_group_add_action( mainActions, act );
6417 }
6419 // Offset spinbox
6420 {
6421 eact = create_adjustment_action(
6422 "OffsetAction",
6423 _("Grow/shrink by"), _("Grow/shrink by:"),
6424 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6425 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6426 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6427 0, 0, 0,
6428 paintbucket_offset_changed, 1, 2);
6429 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6431 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6432 }
6434 /* Auto Gap */
6435 {
6436 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6438 GList* items = 0;
6439 gint count = 0;
6440 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6441 {
6442 GtkTreeIter iter;
6443 gtk_list_store_append( model, &iter );
6444 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6445 count++;
6446 }
6447 g_list_free( items );
6448 items = 0;
6449 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6450 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6451 ege_select_one_action_set_appearance( act2, "compact" );
6452 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6453 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6454 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6455 g_object_set_data( holder, "autogap_action", act2 );
6456 }
6458 /* Reset */
6459 {
6460 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6461 _("Defaults"),
6462 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6463 GTK_STOCK_CLEAR );
6464 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6465 gtk_action_group_add_action( mainActions, act );
6466 gtk_action_set_sensitive( act, TRUE );
6467 }
6469 }
6471 /*
6472 Local Variables:
6473 mode:c++
6474 c-file-style:"stroustrup"
6475 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6476 indent-tabs-mode:nil
6477 fill-column:99
6478 End:
6479 */
6480 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :