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 "live_effects/lpe-line_segment.h"
73 #include "shape-editor.h"
74 #include "tweak-context.h"
75 #include "sp-rect.h"
76 #include "box3d.h"
77 #include "box3d-context.h"
78 #include "sp-star.h"
79 #include "sp-spiral.h"
80 #include "sp-ellipse.h"
81 #include "sp-text.h"
82 #include "sp-flowtext.h"
83 #include "sp-clippath.h"
84 #include "sp-mask.h"
85 #include "style.h"
86 #include "tools-switch.h"
87 #include "selection.h"
88 #include "selection-chemistry.h"
89 #include "document-private.h"
90 #include "desktop-style.h"
91 #include "../libnrtype/font-lister.h"
92 #include "../libnrtype/font-instance.h"
93 #include "../connection-pool.h"
94 #include "../prefs-utils.h"
95 #include "../inkscape-stock.h"
96 #include "icon.h"
97 #include "graphlayout/graphlayout.h"
98 #include "interface.h"
99 #include "shortcuts.h"
101 #include "mod360.h"
103 #include "toolbox.h"
105 #include "flood-context.h"
107 #include "ink-action.h"
108 #include "ege-adjustment-action.h"
109 #include "ege-output-action.h"
110 #include "ege-select-one-action.h"
111 #include "helper/unit-tracker.h"
112 #include "live_effects/lpe-angle_bisector.h"
114 #include "svg/css-ostringstream.h"
116 #include "widgets/calligraphic-profile-rename.h"
118 using Inkscape::UnitTracker;
120 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
121 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
123 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
136 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
139 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
141 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
144 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
145 static Inkscape::IconSize sizeChoices[] = {
146 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
147 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
148 Inkscape::ICON_SIZE_MENU
149 };
150 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
151 return sizeChoices[index];
152 }
154 static struct {
155 gchar const *type_name;
156 gchar const *data_name;
157 sp_verb_t verb;
158 sp_verb_t doubleclick_verb;
159 } const tools[] = {
160 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
161 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
162 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
163 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
164 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
165 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
166 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
167 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
168 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
169 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
170 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
171 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
172 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
173 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
174 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
175 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
176 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
177 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
178 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
179 { NULL, NULL, 0, 0 }
180 };
182 static struct {
183 gchar const *type_name;
184 gchar const *data_name;
185 GtkWidget *(*create_func)(SPDesktop *desktop);
186 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
187 gchar const *ui_name;
188 gint swatch_verb_id;
189 gchar const *swatch_tool;
190 gchar const *swatch_tip;
191 } const aux_toolboxes[] = {
192 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
193 SP_VERB_INVALID, 0, 0},
194 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
195 SP_VERB_INVALID, 0, 0},
196 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
197 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
198 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
199 SP_VERB_INVALID, 0, 0},
200 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
201 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
202 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
203 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
204 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
205 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
206 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
207 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
208 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
209 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
210 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
211 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
212 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
213 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
214 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
215 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
216 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
217 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
218 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
219 SP_VERB_CONTEXT_LPETOOL_PREFS, "tools.lpetool", _("TBD")},
220 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
221 SP_VERB_INVALID, 0, 0},
222 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
223 SP_VERB_INVALID, 0, 0},
224 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
225 SP_VERB_INVALID, 0, 0},
226 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
227 SP_VERB_INVALID, 0, 0},
228 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
229 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
230 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
231 };
233 #define TOOLBAR_SLIDER_HINT "full"
235 static gchar const * ui_descr =
236 "<ui>"
237 " <toolbar name='SelectToolbar'>"
238 " <toolitem action='EditSelectAll' />"
239 " <toolitem action='EditSelectAllInAllLayers' />"
240 " <toolitem action='EditDeselect' />"
241 " <separator />"
242 " <toolitem action='ObjectRotate90CCW' />"
243 " <toolitem action='ObjectRotate90' />"
244 " <toolitem action='ObjectFlipHorizontally' />"
245 " <toolitem action='ObjectFlipVertically' />"
246 " <separator />"
247 " <toolitem action='SelectionToBack' />"
248 " <toolitem action='SelectionLower' />"
249 " <toolitem action='SelectionRaise' />"
250 " <toolitem action='SelectionToFront' />"
251 " <separator />"
252 " <toolitem action='XAction' />"
253 " <toolitem action='YAction' />"
254 " <toolitem action='WidthAction' />"
255 " <toolitem action='LockAction' />"
256 " <toolitem action='HeightAction' />"
257 " <toolitem action='UnitsAction' />"
258 " <separator />"
259 " <toolitem action='transform_affect_label' />"
260 " <toolitem action='transform_stroke' />"
261 " <toolitem action='transform_corners' />"
262 " <toolitem action='transform_gradient' />"
263 " <toolitem action='transform_pattern' />"
264 " </toolbar>"
266 " <toolbar name='NodeToolbar'>"
267 " <toolitem action='NodeInsertAction' />"
268 " <toolitem action='NodeDeleteAction' />"
269 " <separator />"
270 " <toolitem action='NodeJoinAction' />"
271 " <toolitem action='NodeBreakAction' />"
272 " <separator />"
273 " <toolitem action='NodeJoinSegmentAction' />"
274 " <toolitem action='NodeDeleteSegmentAction' />"
275 " <separator />"
276 " <toolitem action='NodeCuspAction' />"
277 " <toolitem action='NodeSmoothAction' />"
278 " <toolitem action='NodeSymmetricAction' />"
279 " <separator />"
280 " <toolitem action='NodeLineAction' />"
281 " <toolitem action='NodeCurveAction' />"
282 " <separator />"
283 " <toolitem action='ObjectToPath' />"
284 " <toolitem action='StrokeToPath' />"
285 " <separator />"
286 " <toolitem action='NodeXAction' />"
287 " <toolitem action='NodeYAction' />"
288 " <toolitem action='NodeUnitsAction' />"
289 " <separator />"
290 " <toolitem action='ObjectEditClipPathAction' />"
291 " <toolitem action='ObjectEditMaskPathAction' />"
292 " <toolitem action='EditNextLPEParameterAction' />"
293 " <separator />"
294 " <toolitem action='NodesShowHandlesAction' />"
295 " <toolitem action='NodesShowHelperpath' />"
296 " </toolbar>"
298 " <toolbar name='TweakToolbar'>"
299 " <toolitem action='TweakWidthAction' />"
300 " <separator />"
301 " <toolitem action='TweakForceAction' />"
302 " <toolitem action='TweakPressureAction' />"
303 " <separator />"
304 " <toolitem action='TweakModeAction' />"
305 " <separator />"
306 " <toolitem action='TweakFidelityAction' />"
307 " <separator />"
308 " <toolitem action='TweakChannelsLabel' />"
309 " <toolitem action='TweakDoH' />"
310 " <toolitem action='TweakDoS' />"
311 " <toolitem action='TweakDoL' />"
312 " <toolitem action='TweakDoO' />"
313 " </toolbar>"
315 " <toolbar name='ZoomToolbar'>"
316 " <toolitem action='ZoomIn' />"
317 " <toolitem action='ZoomOut' />"
318 " <separator />"
319 " <toolitem action='Zoom1:0' />"
320 " <toolitem action='Zoom1:2' />"
321 " <toolitem action='Zoom2:1' />"
322 " <separator />"
323 " <toolitem action='ZoomSelection' />"
324 " <toolitem action='ZoomDrawing' />"
325 " <toolitem action='ZoomPage' />"
326 " <toolitem action='ZoomPageWidth' />"
327 " <separator />"
328 " <toolitem action='ZoomPrev' />"
329 " <toolitem action='ZoomNext' />"
330 " </toolbar>"
332 " <toolbar name='StarToolbar'>"
333 " <separator />"
334 " <toolitem action='StarStateAction' />"
335 " <separator />"
336 " <toolitem action='FlatAction' />"
337 " <separator />"
338 " <toolitem action='MagnitudeAction' />"
339 " <toolitem action='SpokeAction' />"
340 " <toolitem action='RoundednessAction' />"
341 " <toolitem action='RandomizationAction' />"
342 " <separator />"
343 " <toolitem action='StarResetAction' />"
344 " </toolbar>"
346 " <toolbar name='RectToolbar'>"
347 " <toolitem action='RectStateAction' />"
348 " <toolitem action='RectWidthAction' />"
349 " <toolitem action='RectHeightAction' />"
350 " <toolitem action='RadiusXAction' />"
351 " <toolitem action='RadiusYAction' />"
352 " <toolitem action='RectUnitsAction' />"
353 " <separator />"
354 " <toolitem action='RectResetAction' />"
355 " </toolbar>"
357 " <toolbar name='3DBoxToolbar'>"
358 " <toolitem action='3DBoxAngleXAction' />"
359 " <toolitem action='3DBoxVPXStateAction' />"
360 " <separator />"
361 " <toolitem action='3DBoxAngleYAction' />"
362 " <toolitem action='3DBoxVPYStateAction' />"
363 " <separator />"
364 " <toolitem action='3DBoxAngleZAction' />"
365 " <toolitem action='3DBoxVPZStateAction' />"
366 " </toolbar>"
368 " <toolbar name='SpiralToolbar'>"
369 " <toolitem action='SpiralStateAction' />"
370 " <toolitem action='SpiralRevolutionAction' />"
371 " <toolitem action='SpiralExpansionAction' />"
372 " <toolitem action='SpiralT0Action' />"
373 " <separator />"
374 " <toolitem action='SpiralResetAction' />"
375 " </toolbar>"
377 " <toolbar name='PenToolbar'>"
378 " <toolitem action='FreehandModeActionPen' />"
379 " <separator />"
380 " <toolitem action='SetPenShapeAction'/>"
381 " </toolbar>"
383 " <toolbar name='PencilToolbar'>"
384 " <toolitem action='FreehandModeActionPencil' />"
385 " <separator />"
386 " <toolitem action='PencilToleranceAction' />"
387 " <separator />"
388 " <toolitem action='PencilResetAction' />"
389 " <separator />"
390 " <toolitem action='SetPencilShapeAction'/>"
391 " </toolbar>"
393 " <toolbar name='CalligraphyToolbar'>"
394 " <separator />"
395 " <toolitem action='SetProfileAction'/>"
396 " <separator />"
397 " <toolitem action='CalligraphyWidthAction' />"
398 " <toolitem action='PressureAction' />"
399 " <toolitem action='TraceAction' />"
400 " <toolitem action='ThinningAction' />"
401 " <separator />"
402 " <toolitem action='AngleAction' />"
403 " <toolitem action='TiltAction' />"
404 " <toolitem action='FixationAction' />"
405 " <separator />"
406 " <toolitem action='CapRoundingAction' />"
407 " <separator />"
408 " <toolitem action='TremorAction' />"
409 " <toolitem action='WiggleAction' />"
410 " <toolitem action='MassAction' />"
411 " <separator />"
412 " </toolbar>"
414 " <toolbar name='ArcToolbar'>"
415 " <toolitem action='ArcStateAction' />"
416 " <separator />"
417 " <toolitem action='ArcStartAction' />"
418 " <toolitem action='ArcEndAction' />"
419 " <separator />"
420 " <toolitem action='ArcOpenAction' />"
421 " <separator />"
422 " <toolitem action='ArcResetAction' />"
423 " <separator />"
424 " </toolbar>"
426 " <toolbar name='PaintbucketToolbar'>"
427 " <toolitem action='ChannelsAction' />"
428 " <separator />"
429 " <toolitem action='ThresholdAction' />"
430 " <separator />"
431 " <toolitem action='OffsetAction' />"
432 " <toolitem action='PaintbucketUnitsAction' />"
433 " <separator />"
434 " <toolitem action='AutoGapAction' />"
435 " <separator />"
436 " <toolitem action='PaintbucketResetAction' />"
437 " </toolbar>"
439 " <toolbar name='EraserToolbar'>"
440 " <toolitem action='EraserWidthAction' />"
441 " <separator />"
442 " <toolitem action='EraserModeAction' />"
443 " </toolbar>"
445 " <toolbar name='LPEToolToolbar'>"
446 " <toolitem action='LPEToolModeAction' />"
447 " <separator />"
448 " <toolitem action='LPEShowBBoxAction' />"
449 " <toolitem action='LPEBBoxFromSelectionAction' />"
450 " <separator />"
451 " <toolitem action='LPELineSegmentAction' />"
452 " <separator />"
453 " <toolitem action='LPEMeasuringAction' />"
454 " <toolitem action='LPEToolUnitsAction' />"
455 " </toolbar>"
457 " <toolbar name='DropperToolbar'>"
458 " <toolitem action='DropperOpacityAction' />"
459 " <toolitem action='DropperPickAlphaAction' />"
460 " <toolitem action='DropperSetAlphaAction' />"
461 " </toolbar>"
463 " <toolbar name='ConnectorToolbar'>"
464 " <toolitem action='ConnectorAvoidAction' />"
465 " <toolitem action='ConnectorIgnoreAction' />"
466 " <toolitem action='ConnectorSpacingAction' />"
467 " <toolitem action='ConnectorGraphAction' />"
468 " <toolitem action='ConnectorLengthAction' />"
469 " <toolitem action='ConnectorDirectedAction' />"
470 " <toolitem action='ConnectorOverlapAction' />"
471 " </toolbar>"
473 "</ui>"
474 ;
476 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
478 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
480 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
481 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
484 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
486 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
487 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
490 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
491 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
492 Inkscape::UI::View::View *view, GtkTooltips *tt);
494 class VerbAction : public Gtk::Action {
495 public:
496 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
498 virtual ~VerbAction();
499 virtual void set_active(bool active = true);
501 protected:
502 virtual Gtk::Widget* create_menu_item_vfunc();
503 virtual Gtk::Widget* create_tool_item_vfunc();
505 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
506 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
508 virtual void on_activate();
510 private:
511 Inkscape::Verb* verb;
512 Inkscape::Verb* verb2;
513 Inkscape::UI::View::View *view;
514 GtkTooltips *tooltips;
515 bool active;
517 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
518 };
521 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
522 {
523 Glib::RefPtr<VerbAction> result;
524 SPAction *action = verb->get_action(view);
525 if ( action ) {
526 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
527 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
528 }
530 return result;
531 }
533 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
534 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
535 verb(verb),
536 verb2(verb2),
537 view(view),
538 tooltips(tooltips),
539 active(false)
540 {
541 }
543 VerbAction::~VerbAction()
544 {
545 }
547 Gtk::Widget* VerbAction::create_menu_item_vfunc()
548 {
549 // First call in to get the icon rendered if present in SVG
550 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
551 delete widget;
552 widget = 0;
554 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
555 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
556 return widg;
557 }
559 Gtk::Widget* VerbAction::create_tool_item_vfunc()
560 {
561 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
562 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
563 GtkWidget* toolbox = 0;
564 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
565 SP_BUTTON_TYPE_TOGGLE,
566 verb,
567 verb2,
568 view,
569 tooltips );
570 if ( active ) {
571 sp_button_toggle_set_down( SP_BUTTON(button), active);
572 }
573 gtk_widget_show_all( button );
574 Gtk::Widget* wrapped = Glib::wrap(button);
575 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
576 holder->add(*wrapped);
578 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
579 return holder;
580 }
582 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
583 {
584 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
585 Gtk::Action::connect_proxy_vfunc(proxy);
586 }
588 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
589 {
590 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
591 Gtk::Action::disconnect_proxy_vfunc(proxy);
592 }
594 void VerbAction::set_active(bool active)
595 {
596 this->active = active;
597 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
598 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
599 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
600 if (ti) {
601 // *should* have one child that is the SPButton
602 Gtk::Widget* child = ti->get_child();
603 if ( child && SP_IS_BUTTON(child->gobj()) ) {
604 SPButton* button = SP_BUTTON(child->gobj());
605 sp_button_toggle_set_down( button, active );
606 }
607 }
608 }
609 }
611 void VerbAction::on_activate()
612 {
613 if ( verb ) {
614 SPAction *action = verb->get_action(view);
615 if ( action ) {
616 sp_action_perform(action, 0);
617 }
618 }
619 }
621 /* Global text entry widgets necessary for update */
622 /* GtkWidget *dropper_rgb_entry,
623 *dropper_opacity_entry ; */
624 // should be made a private member once this is converted to class
626 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
627 connection->disconnect();
628 delete connection;
629 }
631 static void purge_repr_listener( GObject* obj, GObject* tbl )
632 {
633 (void)obj;
634 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
635 if (oldrepr) { // remove old listener
636 sp_repr_remove_listener_by_data(oldrepr, tbl);
637 Inkscape::GC::release(oldrepr);
638 oldrepr = 0;
639 g_object_set_data( tbl, "repr", NULL );
640 }
641 }
643 GtkWidget *
644 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
645 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
646 Inkscape::UI::View::View *view, GtkTooltips *tt)
647 {
648 SPAction *action = verb->get_action(view);
649 if (!action) return NULL;
651 SPAction *doubleclick_action;
652 if (doubleclick_verb)
653 doubleclick_action = doubleclick_verb->get_action(view);
654 else
655 doubleclick_action = NULL;
657 /* fixme: Handle sensitive/unsensitive */
658 /* fixme: Implement sp_button_new_from_action */
659 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
660 gtk_widget_show(b);
663 unsigned int shortcut = sp_shortcut_get_primary(verb);
664 if (shortcut) {
665 gchar key[256];
666 sp_ui_shortcut_string(shortcut, key);
667 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
668 if ( t ) {
669 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
670 }
671 g_free(tip);
672 } else {
673 if ( t ) {
674 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
675 }
676 }
678 return b;
679 }
682 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
683 {
684 SPAction* targetAction = SP_ACTION(user_data);
685 if ( targetAction ) {
686 sp_action_perform( targetAction, NULL );
687 }
688 }
690 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
691 {
692 if ( data ) {
693 GtkAction* act = GTK_ACTION(data);
694 gtk_action_set_sensitive( act, sensitive );
695 }
696 }
698 static SPActionEventVector action_event_vector = {
699 {NULL},
700 NULL,
701 NULL,
702 sp_action_action_set_sensitive,
703 NULL,
704 NULL
705 };
707 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
708 {
709 GtkAction* act = 0;
711 SPAction* targetAction = verb->get_action(view);
712 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
713 act = GTK_ACTION(inky);
714 gtk_action_set_sensitive( act, targetAction->sensitive );
716 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
718 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
719 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
721 return act;
722 }
724 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
725 {
726 Inkscape::UI::View::View *view = desktop;
727 gint verbsToUse[] = {
728 // disabled until we have icons for them:
729 //find
730 //SP_VERB_EDIT_TILE,
731 //SP_VERB_EDIT_UNTILE,
732 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
733 SP_VERB_DIALOG_DISPLAY,
734 SP_VERB_DIALOG_FILL_STROKE,
735 SP_VERB_DIALOG_NAMEDVIEW,
736 SP_VERB_DIALOG_TEXT,
737 SP_VERB_DIALOG_XML_EDITOR,
738 SP_VERB_EDIT_CLONE,
739 SP_VERB_EDIT_COPY,
740 SP_VERB_EDIT_CUT,
741 SP_VERB_EDIT_DUPLICATE,
742 SP_VERB_EDIT_PASTE,
743 SP_VERB_EDIT_REDO,
744 SP_VERB_EDIT_UNDO,
745 SP_VERB_EDIT_UNLINK_CLONE,
746 SP_VERB_FILE_EXPORT,
747 SP_VERB_FILE_IMPORT,
748 SP_VERB_FILE_NEW,
749 SP_VERB_FILE_OPEN,
750 SP_VERB_FILE_PRINT,
751 SP_VERB_FILE_SAVE,
752 SP_VERB_OBJECT_TO_CURVE,
753 SP_VERB_SELECTION_GROUP,
754 SP_VERB_SELECTION_OUTLINE,
755 SP_VERB_SELECTION_UNGROUP,
756 SP_VERB_ZOOM_1_1,
757 SP_VERB_ZOOM_1_2,
758 SP_VERB_ZOOM_2_1,
759 SP_VERB_ZOOM_DRAWING,
760 SP_VERB_ZOOM_IN,
761 SP_VERB_ZOOM_NEXT,
762 SP_VERB_ZOOM_OUT,
763 SP_VERB_ZOOM_PAGE,
764 SP_VERB_ZOOM_PAGE_WIDTH,
765 SP_VERB_ZOOM_PREV,
766 SP_VERB_ZOOM_SELECTION,
767 };
769 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
771 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
772 Glib::RefPtr<Gtk::ActionGroup> mainActions;
773 if ( groups.find(desktop) != groups.end() ) {
774 mainActions = groups[desktop];
775 }
777 if ( !mainActions ) {
778 mainActions = Gtk::ActionGroup::create("main");
779 groups[desktop] = mainActions;
780 }
782 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
783 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
784 if ( verb ) {
785 if (!mainActions->get_action(verb->get_id())) {
786 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
787 mainActions->add(Glib::wrap(act));
788 }
789 }
790 }
792 if ( !mainActions->get_action("ToolZoom") ) {
793 GtkTooltips *tt = gtk_tooltips_new();
794 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
795 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
796 if ( va ) {
797 mainActions->add(va);
798 if ( i == 0 ) {
799 va->set_active(true);
800 }
801 }
802 }
803 }
806 return mainActions;
807 }
810 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
811 {
812 gtk_widget_set_size_request( widget,
813 widget->allocation.width,
814 widget->allocation.height );
815 }
817 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
818 {
819 gtk_widget_set_size_request( widget, -1, -1 );
820 }
824 GtkWidget *
825 sp_tool_toolbox_new()
826 {
827 GtkTooltips *tt = gtk_tooltips_new();
828 GtkWidget* tb = gtk_toolbar_new();
829 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
830 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
832 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
833 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
835 gtk_widget_set_sensitive(tb, FALSE);
837 GtkWidget *hb = gtk_handle_box_new();
838 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
839 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
840 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
842 gtk_container_add(GTK_CONTAINER(hb), tb);
843 gtk_widget_show(GTK_WIDGET(tb));
845 sigc::connection* conn = new sigc::connection;
846 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
848 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
849 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
851 return hb;
852 }
854 GtkWidget *
855 sp_aux_toolbox_new()
856 {
857 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
859 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
861 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
863 gtk_widget_set_sensitive(tb, FALSE);
865 GtkWidget *hb = gtk_handle_box_new();
866 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
867 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
868 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
870 gtk_container_add(GTK_CONTAINER(hb), tb);
871 gtk_widget_show(GTK_WIDGET(tb));
873 sigc::connection* conn = new sigc::connection;
874 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
876 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
877 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
879 return hb;
880 }
882 //####################################
883 //# Commands Bar
884 //####################################
886 GtkWidget *
887 sp_commands_toolbox_new()
888 {
889 GtkWidget *tb = gtk_toolbar_new();
891 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
892 gtk_widget_set_sensitive(tb, FALSE);
894 GtkWidget *hb = gtk_handle_box_new();
895 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
896 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
897 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
899 gtk_container_add(GTK_CONTAINER(hb), tb);
900 gtk_widget_show(GTK_WIDGET(tb));
902 sigc::connection* conn = new sigc::connection;
903 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
905 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
906 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
908 return hb;
909 }
912 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
913 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
914 gchar const *path, gchar const *data, gdouble def,
915 GtkWidget *focusTarget,
916 GtkWidget *us,
917 GObject *dataKludge,
918 gboolean altx, gchar const *altx_mark,
919 gdouble lower, gdouble upper, gdouble step, gdouble page,
920 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
921 void (*callback)(GtkAdjustment *, GObject *),
922 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
923 {
924 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
925 lower, upper, step, page, page ) );
926 if (us) {
927 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
928 }
930 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
932 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
933 if ( shortLabel ) {
934 g_object_set( act, "short_label", shortLabel, NULL );
935 }
937 if ( (descrCount > 0) && descrLabels && descrValues ) {
938 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
939 }
941 if ( focusTarget ) {
942 ege_adjustment_action_set_focuswidget( act, focusTarget );
943 }
945 if ( altx && altx_mark ) {
946 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
947 }
949 if ( dataKludge ) {
950 g_object_set_data( dataKludge, data, adj );
951 }
953 // Using a cast just to make sure we pass in the right kind of function pointer
954 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
956 return act;
957 }
960 //####################################
961 //# node editing callbacks
962 //####################################
964 /**
965 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
966 */
967 static ShapeEditor *get_current_shape_editor()
968 {
969 if (!SP_ACTIVE_DESKTOP) {
970 return NULL;
971 }
973 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
975 if (!SP_IS_NODE_CONTEXT(event_context)) {
976 return NULL;
977 }
979 return SP_NODE_CONTEXT(event_context)->shape_editor;
980 }
983 void
984 sp_node_path_edit_add(void)
985 {
986 ShapeEditor *shape_editor = get_current_shape_editor();
987 if (shape_editor) shape_editor->add_node();
988 }
990 void
991 sp_node_path_edit_delete(void)
992 {
993 ShapeEditor *shape_editor = get_current_shape_editor();
994 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
995 }
997 void
998 sp_node_path_edit_delete_segment(void)
999 {
1000 ShapeEditor *shape_editor = get_current_shape_editor();
1001 if (shape_editor) shape_editor->delete_segment();
1002 }
1004 void
1005 sp_node_path_edit_break(void)
1006 {
1007 ShapeEditor *shape_editor = get_current_shape_editor();
1008 if (shape_editor) shape_editor->break_at_nodes();
1009 }
1011 void
1012 sp_node_path_edit_join(void)
1013 {
1014 ShapeEditor *shape_editor = get_current_shape_editor();
1015 if (shape_editor) shape_editor->join_nodes();
1016 }
1018 void
1019 sp_node_path_edit_join_segment(void)
1020 {
1021 ShapeEditor *shape_editor = get_current_shape_editor();
1022 if (shape_editor) shape_editor->join_segments();
1023 }
1025 void
1026 sp_node_path_edit_toline(void)
1027 {
1028 ShapeEditor *shape_editor = get_current_shape_editor();
1029 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1030 }
1032 void
1033 sp_node_path_edit_tocurve(void)
1034 {
1035 ShapeEditor *shape_editor = get_current_shape_editor();
1036 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1037 }
1039 void
1040 sp_node_path_edit_cusp(void)
1041 {
1042 ShapeEditor *shape_editor = get_current_shape_editor();
1043 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1044 }
1046 void
1047 sp_node_path_edit_smooth(void)
1048 {
1049 ShapeEditor *shape_editor = get_current_shape_editor();
1050 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1051 }
1053 void
1054 sp_node_path_edit_symmetrical(void)
1055 {
1056 ShapeEditor *shape_editor = get_current_shape_editor();
1057 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1058 }
1060 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1061 bool show = gtk_toggle_action_get_active( act );
1062 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1063 ShapeEditor *shape_editor = get_current_shape_editor();
1064 if (shape_editor) shape_editor->show_handles(show);
1065 }
1067 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1068 bool show = gtk_toggle_action_get_active( act );
1069 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1070 ShapeEditor *shape_editor = get_current_shape_editor();
1071 if (shape_editor) shape_editor->show_helperpath(show);
1072 }
1074 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1075 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1076 }
1078 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1079 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1080 }
1082 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1083 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1084 }
1086 /* is called when the node selection is modified */
1087 static void
1088 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1089 {
1090 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1091 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1092 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1093 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1095 // quit if run by the attr_changed listener
1096 if (g_object_get_data( tbl, "freeze" )) {
1097 return;
1098 }
1100 // in turn, prevent listener from responding
1101 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1103 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1104 SPUnit const *unit = tracker->getActiveUnit();
1106 ShapeEditor *shape_editor = get_current_shape_editor();
1107 if (shape_editor && shape_editor->has_nodepath()) {
1108 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1109 int n_selected = 0;
1110 if (nodepath) {
1111 n_selected = nodepath->numSelected();
1112 }
1114 if (n_selected == 0) {
1115 gtk_action_set_sensitive(xact, FALSE);
1116 gtk_action_set_sensitive(yact, FALSE);
1117 } else {
1118 gtk_action_set_sensitive(xact, TRUE);
1119 gtk_action_set_sensitive(yact, TRUE);
1120 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1121 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1123 if (n_selected == 1) {
1124 Geom::Point sel_node = nodepath->singleSelectedCoords();
1125 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1126 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1127 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1128 }
1129 } else {
1130 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1131 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1132 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1133 /* Note: Currently x and y will always have a value, even if the coordinates of the
1134 selected nodes don't coincide (in this case we use the coordinates of the center
1135 of the bounding box). So the entries are never set to zero. */
1136 // FIXME: Maybe we should clear the entry if several nodes are selected
1137 // instead of providing a kind of average value
1138 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1139 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1140 }
1141 }
1142 }
1143 } else {
1144 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1145 gtk_action_set_sensitive(xact, FALSE);
1146 gtk_action_set_sensitive(yact, FALSE);
1147 }
1149 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1150 }
1152 static void
1153 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1154 {
1155 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1157 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1158 SPUnit const *unit = tracker->getActiveUnit();
1160 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1161 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1162 }
1164 // quit if run by the attr_changed listener
1165 if (g_object_get_data( tbl, "freeze" )) {
1166 return;
1167 }
1169 // in turn, prevent listener from responding
1170 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1172 ShapeEditor *shape_editor = get_current_shape_editor();
1173 if (shape_editor && shape_editor->has_nodepath()) {
1174 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1175 if (!strcmp(value_name, "x")) {
1176 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1177 }
1178 if (!strcmp(value_name, "y")) {
1179 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1180 }
1181 }
1183 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1184 }
1186 static void
1187 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1188 {
1189 sp_node_path_value_changed(adj, tbl, "x");
1190 }
1192 static void
1193 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1194 {
1195 sp_node_path_value_changed(adj, tbl, "y");
1196 }
1198 void
1199 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1200 {
1201 {
1202 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1203 SPItem *item = selection->singleItem();
1204 if (item && SP_IS_LPE_ITEM(item)) {
1205 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1206 gtk_action_set_sensitive(w, TRUE);
1207 } else {
1208 gtk_action_set_sensitive(w, FALSE);
1209 }
1210 } else {
1211 gtk_action_set_sensitive(w, FALSE);
1212 }
1213 }
1215 {
1216 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1217 SPItem *item = selection->singleItem();
1218 if (item && item->clip_ref && item->clip_ref->getObject()) {
1219 gtk_action_set_sensitive(w, TRUE);
1220 } else {
1221 gtk_action_set_sensitive(w, FALSE);
1222 }
1223 }
1225 {
1226 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1227 SPItem *item = selection->singleItem();
1228 if (item && item->mask_ref && item->mask_ref->getObject()) {
1229 gtk_action_set_sensitive(w, TRUE);
1230 } else {
1231 gtk_action_set_sensitive(w, FALSE);
1232 }
1233 }
1234 }
1236 void
1237 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1238 {
1239 sp_node_toolbox_sel_changed (selection, tbl);
1240 }
1244 //################################
1245 //## Node Editing Toolbox ##
1246 //################################
1248 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1249 {
1250 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1251 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1252 g_object_set_data( holder, "tracker", tracker );
1254 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1256 {
1257 InkAction* inky = ink_action_new( "NodeInsertAction",
1258 _("Insert node"),
1259 _("Insert new nodes into selected segments"),
1260 "node_insert",
1261 secondarySize );
1262 g_object_set( inky, "short_label", _("Insert"), NULL );
1263 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1264 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1265 }
1267 {
1268 InkAction* inky = ink_action_new( "NodeDeleteAction",
1269 _("Delete node"),
1270 _("Delete selected nodes"),
1271 "node_delete",
1272 secondarySize );
1273 g_object_set( inky, "short_label", _("Delete"), NULL );
1274 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1275 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1276 }
1278 {
1279 InkAction* inky = ink_action_new( "NodeJoinAction",
1280 _("Join endnodes"),
1281 _("Join selected endnodes"),
1282 "node_join",
1283 secondarySize );
1284 g_object_set( inky, "short_label", _("Join"), NULL );
1285 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1286 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1287 }
1289 {
1290 InkAction* inky = ink_action_new( "NodeBreakAction",
1291 _("Break nodes"),
1292 _("Break path at selected nodes"),
1293 "node_break",
1294 secondarySize );
1295 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1296 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1297 }
1300 {
1301 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1302 _("Join with segment"),
1303 _("Join selected endnodes with a new segment"),
1304 "node_join_segment",
1305 secondarySize );
1306 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1307 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1308 }
1310 {
1311 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1312 _("Delete segment"),
1313 _("Delete segment between two non-endpoint nodes"),
1314 "node_delete_segment",
1315 secondarySize );
1316 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1317 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1318 }
1320 {
1321 InkAction* inky = ink_action_new( "NodeCuspAction",
1322 _("Node Cusp"),
1323 _("Make selected nodes corner"),
1324 "node_cusp",
1325 secondarySize );
1326 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1327 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328 }
1330 {
1331 InkAction* inky = ink_action_new( "NodeSmoothAction",
1332 _("Node Smooth"),
1333 _("Make selected nodes smooth"),
1334 "node_smooth",
1335 secondarySize );
1336 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1337 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338 }
1340 {
1341 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1342 _("Node Symmetric"),
1343 _("Make selected nodes symmetric"),
1344 "node_symmetric",
1345 secondarySize );
1346 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1347 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348 }
1350 {
1351 InkAction* inky = ink_action_new( "NodeLineAction",
1352 _("Node Line"),
1353 _("Make selected segments lines"),
1354 "node_line",
1355 secondarySize );
1356 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1357 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1358 }
1360 {
1361 InkAction* inky = ink_action_new( "NodeCurveAction",
1362 _("Node Curve"),
1363 _("Make selected segments curves"),
1364 "node_curve",
1365 secondarySize );
1366 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1367 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1368 }
1370 {
1371 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1372 _("Show Handles"),
1373 _("Show the Bezier handles of selected nodes"),
1374 "nodes_show_handles",
1375 Inkscape::ICON_SIZE_DECORATION );
1376 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1377 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1378 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1379 }
1381 {
1382 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1383 _("Show Outline"),
1384 _("Show the outline of the path"),
1385 "nodes_show_helperpath",
1386 Inkscape::ICON_SIZE_DECORATION );
1387 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1388 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1389 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1390 }
1392 {
1393 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1394 _("Next path effect parameter"),
1395 _("Show next path effect parameter for editing"),
1396 "edit_next_parameter",
1397 Inkscape::ICON_SIZE_DECORATION );
1398 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1399 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1400 g_object_set_data( holder, "nodes_lpeedit", inky);
1401 }
1403 {
1404 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1405 _("Edit clipping path"),
1406 _("Edit the clipping path of the object"),
1407 "nodeedit-clippath",
1408 Inkscape::ICON_SIZE_DECORATION );
1409 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1410 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1411 g_object_set_data( holder, "nodes_clippathedit", inky);
1412 }
1414 {
1415 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1416 _("Edit mask path"),
1417 _("Edit the mask of the object"),
1418 "nodeedit-mask",
1419 Inkscape::ICON_SIZE_DECORATION );
1420 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1421 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1422 g_object_set_data( holder, "nodes_maskedit", inky);
1423 }
1425 /* X coord of selected node(s) */
1426 {
1427 EgeAdjustmentAction* eact = 0;
1428 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1429 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1430 eact = create_adjustment_action( "NodeXAction",
1431 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1432 "tools.nodes", "Xcoord", 0,
1433 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1434 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1435 labels, values, G_N_ELEMENTS(labels),
1436 sp_node_path_x_value_changed );
1437 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1438 g_object_set_data( holder, "nodes_x_action", eact );
1439 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1440 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1441 }
1443 /* Y coord of selected node(s) */
1444 {
1445 EgeAdjustmentAction* eact = 0;
1446 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1447 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1448 eact = create_adjustment_action( "NodeYAction",
1449 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1450 "tools.nodes", "Ycoord", 0,
1451 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1452 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1453 labels, values, G_N_ELEMENTS(labels),
1454 sp_node_path_y_value_changed );
1455 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1456 g_object_set_data( holder, "nodes_y_action", eact );
1457 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1458 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1459 }
1461 // add the units menu
1462 {
1463 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1464 gtk_action_group_add_action( mainActions, act );
1465 }
1468 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1470 //watch selection
1471 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1473 sigc::connection *c_selection_changed =
1474 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1475 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1476 pool->add_connection ("selection-changed", c_selection_changed);
1478 sigc::connection *c_selection_modified =
1479 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1480 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1481 pool->add_connection ("selection-modified", c_selection_modified);
1483 sigc::connection *c_subselection_changed =
1484 new sigc::connection (desktop->connectToolSubselectionChanged
1485 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1486 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1488 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1490 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1491 } // end of sp_node_toolbox_prep()
1494 //########################
1495 //## Zoom Toolbox ##
1496 //########################
1498 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1499 {
1500 // no custom GtkAction setup needed
1501 } // end of sp_zoom_toolbox_prep()
1503 void
1504 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1505 {
1506 toolbox_set_desktop(toolbox,
1507 desktop,
1508 setup_tool_toolbox,
1509 update_tool_toolbox,
1510 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1511 "event_context_connection")));
1512 }
1515 void
1516 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1517 {
1518 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1519 desktop,
1520 setup_aux_toolbox,
1521 update_aux_toolbox,
1522 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1523 "event_context_connection")));
1524 }
1526 void
1527 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1528 {
1529 toolbox_set_desktop(toolbox,
1530 desktop,
1531 setup_commands_toolbox,
1532 update_commands_toolbox,
1533 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1534 "event_context_connection")));
1535 }
1537 static void
1538 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1539 {
1540 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1541 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1543 if (old_desktop) {
1544 GList *children, *iter;
1546 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1547 for ( iter = children ; iter ; iter = iter->next ) {
1548 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1549 }
1550 g_list_free(children);
1551 }
1553 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1555 if (desktop) {
1556 gtk_widget_set_sensitive(toolbox, TRUE);
1557 setup_func(toolbox, desktop);
1558 update_func(desktop, desktop->event_context, toolbox);
1559 *conn = desktop->connectEventContextChanged
1560 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1561 } else {
1562 gtk_widget_set_sensitive(toolbox, FALSE);
1563 }
1565 } // end of toolbox_set_desktop()
1568 static void
1569 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1570 {
1571 gchar const * descr =
1572 "<ui>"
1573 " <toolbar name='ToolToolbar'>"
1574 " <toolitem action='ToolSelector' />"
1575 " <toolitem action='ToolNode' />"
1576 " <toolitem action='ToolTweak' />"
1577 " <toolitem action='ToolZoom' />"
1578 " <toolitem action='ToolRect' />"
1579 " <toolitem action='Tool3DBox' />"
1580 " <toolitem action='ToolArc' />"
1581 " <toolitem action='ToolStar' />"
1582 " <toolitem action='ToolSpiral' />"
1583 " <toolitem action='ToolPencil' />"
1584 " <toolitem action='ToolPen' />"
1585 " <toolitem action='ToolCalligraphic' />"
1586 " <toolitem action='ToolEraser' />"
1587 // " <toolitem action='ToolLPETool' />"
1588 " <toolitem action='ToolPaintBucket' />"
1589 " <toolitem action='ToolText' />"
1590 " <toolitem action='ToolConnector' />"
1591 " <toolitem action='ToolGradient' />"
1592 " <toolitem action='ToolDropper' />"
1593 " </toolbar>"
1594 "</ui>";
1595 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1596 GtkUIManager* mgr = gtk_ui_manager_new();
1597 GError* errVal = 0;
1599 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1600 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1602 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1603 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1604 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1605 }
1606 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1607 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1609 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1610 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1612 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1614 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1615 if ( child ) {
1616 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1617 }
1619 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1620 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1621 }
1624 static void
1625 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1626 {
1627 gchar const *const tname = ( eventcontext
1628 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1629 : NULL );
1630 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1632 for (int i = 0 ; tools[i].type_name ; i++ ) {
1633 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1634 if ( act ) {
1635 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1636 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1637 if ( verbAct ) {
1638 verbAct->set_active(setActive);
1639 }
1640 }
1641 }
1642 }
1644 static void
1645 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1646 {
1647 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1648 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1649 GtkUIManager* mgr = gtk_ui_manager_new();
1650 GError* errVal = 0;
1651 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1652 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1654 std::map<std::string, GtkWidget*> dataHolders;
1656 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1657 if ( aux_toolboxes[i].prep_func ) {
1658 // converted to GtkActions and UIManager
1660 GtkWidget* kludge = gtk_toolbar_new();
1661 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1662 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1663 dataHolders[aux_toolboxes[i].type_name] = kludge;
1664 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1665 } else {
1667 GtkWidget *sub_toolbox = 0;
1668 if (aux_toolboxes[i].create_func == NULL)
1669 sub_toolbox = sp_empty_toolbox_new(desktop);
1670 else {
1671 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1672 }
1674 gtk_size_group_add_widget( grouper, sub_toolbox );
1676 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1677 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1679 }
1680 }
1682 // Second pass to create toolbars *after* all GtkActions are created
1683 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1684 if ( aux_toolboxes[i].prep_func ) {
1685 // converted to GtkActions and UIManager
1687 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1689 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1690 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1692 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1693 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1694 g_free( tmp );
1695 tmp = 0;
1697 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1698 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1699 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1700 }
1701 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1704 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1706 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1707 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1708 swatch->setDesktop( desktop );
1709 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1710 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1711 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1712 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 );
1713 }
1715 gtk_widget_show_all( holder );
1716 sp_set_font_size_smaller( holder );
1718 gtk_size_group_add_widget( grouper, holder );
1720 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1721 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1722 }
1723 }
1725 g_object_unref( G_OBJECT(grouper) );
1726 }
1728 static void
1729 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1730 {
1731 gchar const *tname = ( eventcontext
1732 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1733 : NULL );
1734 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1735 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1736 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1737 gtk_widget_show_all(sub_toolbox);
1738 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1739 } else {
1740 gtk_widget_hide(sub_toolbox);
1741 }
1742 }
1743 }
1745 static void
1746 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1747 {
1748 gchar const * descr =
1749 "<ui>"
1750 " <toolbar name='CommandsToolbar'>"
1751 " <toolitem action='FileNew' />"
1752 " <toolitem action='FileOpen' />"
1753 " <toolitem action='FileSave' />"
1754 " <toolitem action='FilePrint' />"
1755 " <separator />"
1756 " <toolitem action='FileImport' />"
1757 " <toolitem action='FileExport' />"
1758 " <separator />"
1759 " <toolitem action='EditUndo' />"
1760 " <toolitem action='EditRedo' />"
1761 " <separator />"
1762 " <toolitem action='EditCopy' />"
1763 " <toolitem action='EditCut' />"
1764 " <toolitem action='EditPaste' />"
1765 " <separator />"
1766 " <toolitem action='ZoomSelection' />"
1767 " <toolitem action='ZoomDrawing' />"
1768 " <toolitem action='ZoomPage' />"
1769 " <separator />"
1770 " <toolitem action='EditDuplicate' />"
1771 " <toolitem action='EditClone' />"
1772 " <toolitem action='EditUnlinkClone' />"
1773 " <separator />"
1774 " <toolitem action='SelectionGroup' />"
1775 " <toolitem action='SelectionUnGroup' />"
1776 " <separator />"
1777 " <toolitem action='DialogFillStroke' />"
1778 " <toolitem action='DialogText' />"
1779 " <toolitem action='DialogXMLEditor' />"
1780 " <toolitem action='DialogAlignDistribute' />"
1781 " <separator />"
1782 " <toolitem action='DialogPreferences' />"
1783 " <toolitem action='DialogDocumentProperties' />"
1784 " </toolbar>"
1785 "</ui>";
1786 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1789 GtkUIManager* mgr = gtk_ui_manager_new();
1790 GError* errVal = 0;
1792 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1793 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1795 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1796 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1797 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1798 }
1800 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1801 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1803 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1804 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1807 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1809 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1810 if ( child ) {
1811 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1812 }
1814 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1815 }
1817 static void
1818 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1819 {
1820 }
1822 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1823 {
1824 gtk_widget_show(toolbox_toplevel);
1825 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1827 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1828 if (!shown_toolbox) {
1829 return;
1830 }
1831 gtk_widget_show(toolbox);
1833 gtk_widget_show_all(shown_toolbox);
1834 }
1836 static GtkWidget *
1837 sp_empty_toolbox_new(SPDesktop *desktop)
1838 {
1839 GtkWidget *tbl = gtk_toolbar_new();
1840 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1841 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1843 gtk_widget_show_all(tbl);
1844 sp_set_font_size_smaller (tbl);
1846 return tbl;
1847 }
1849 #define MODE_LABEL_WIDTH 70
1851 //########################
1852 //## Star ##
1853 //########################
1855 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1856 {
1857 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1859 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1860 // do not remember prefs if this call is initiated by an undo change, because undoing object
1861 // creation sets bogus values to its attributes before it is deleted
1862 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1863 }
1865 // quit if run by the attr_changed listener
1866 if (g_object_get_data( dataKludge, "freeze" )) {
1867 return;
1868 }
1870 // in turn, prevent listener from responding
1871 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1873 bool modmade = false;
1875 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1876 GSList const *items = selection->itemList();
1877 for (; items != NULL; items = items->next) {
1878 if (SP_IS_STAR((SPItem *) items->data)) {
1879 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1880 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1881 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1882 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1883 + M_PI / (gint)adj->value));
1884 SP_OBJECT((SPItem *) items->data)->updateRepr();
1885 modmade = true;
1886 }
1887 }
1888 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1889 _("Star: Change number of corners"));
1891 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1892 }
1894 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1895 {
1896 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1898 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1899 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1900 }
1902 // quit if run by the attr_changed listener
1903 if (g_object_get_data( dataKludge, "freeze" )) {
1904 return;
1905 }
1907 // in turn, prevent listener from responding
1908 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1910 bool modmade = false;
1911 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1912 GSList const *items = selection->itemList();
1913 for (; items != NULL; items = items->next) {
1914 if (SP_IS_STAR((SPItem *) items->data)) {
1915 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1917 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1918 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1919 if (r2 < r1) {
1920 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1921 } else {
1922 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1923 }
1925 SP_OBJECT((SPItem *) items->data)->updateRepr();
1926 modmade = true;
1927 }
1928 }
1930 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1931 _("Star: Change spoke ratio"));
1933 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1934 }
1936 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1937 {
1938 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1939 bool flat = ege_select_one_action_get_active( act ) == 0;
1941 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1942 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1943 flat ? "true" : "false" );
1944 }
1946 // quit if run by the attr_changed listener
1947 if (g_object_get_data( dataKludge, "freeze" )) {
1948 return;
1949 }
1951 // in turn, prevent listener from responding
1952 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1954 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1955 GSList const *items = selection->itemList();
1956 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1957 bool modmade = false;
1959 if ( prop_action ) {
1960 gtk_action_set_sensitive( prop_action, !flat );
1961 }
1963 for (; items != NULL; items = items->next) {
1964 if (SP_IS_STAR((SPItem *) items->data)) {
1965 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1966 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1967 SP_OBJECT((SPItem *) items->data)->updateRepr();
1968 modmade = true;
1969 }
1970 }
1972 if (modmade) {
1973 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1974 flat ? _("Make polygon") : _("Make star"));
1975 }
1977 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1978 }
1980 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1981 {
1982 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1984 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1985 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1986 }
1988 // quit if run by the attr_changed listener
1989 if (g_object_get_data( dataKludge, "freeze" )) {
1990 return;
1991 }
1993 // in turn, prevent listener from responding
1994 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1996 bool modmade = false;
1998 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1999 GSList const *items = selection->itemList();
2000 for (; items != NULL; items = items->next) {
2001 if (SP_IS_STAR((SPItem *) items->data)) {
2002 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2003 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2004 SP_OBJECT(items->data)->updateRepr();
2005 modmade = true;
2006 }
2007 }
2008 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2009 _("Star: Change rounding"));
2011 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2012 }
2014 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2015 {
2016 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2018 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2019 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2020 }
2022 // quit if run by the attr_changed listener
2023 if (g_object_get_data( dataKludge, "freeze" )) {
2024 return;
2025 }
2027 // in turn, prevent listener from responding
2028 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2030 bool modmade = false;
2032 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2033 GSList const *items = selection->itemList();
2034 for (; items != NULL; items = items->next) {
2035 if (SP_IS_STAR((SPItem *) items->data)) {
2036 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2037 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2038 SP_OBJECT(items->data)->updateRepr();
2039 modmade = true;
2040 }
2041 }
2042 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2043 _("Star: Change randomization"));
2045 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2046 }
2049 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2050 gchar const */*old_value*/, gchar const */*new_value*/,
2051 bool /*is_interactive*/, gpointer data)
2052 {
2053 GtkWidget *tbl = GTK_WIDGET(data);
2055 // quit if run by the _changed callbacks
2056 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2057 return;
2058 }
2060 // in turn, prevent callbacks from responding
2061 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2063 GtkAdjustment *adj = 0;
2065 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2066 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2068 if (!strcmp(name, "inkscape:randomized")) {
2069 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2070 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2071 } else if (!strcmp(name, "inkscape:rounded")) {
2072 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2073 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2074 } else if (!strcmp(name, "inkscape:flatsided")) {
2075 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2076 char const *flatsides = repr->attribute("inkscape:flatsided");
2077 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2078 if ( flatsides && !strcmp(flatsides,"false") ) {
2079 ege_select_one_action_set_active( flat_action, 1 );
2080 gtk_action_set_sensitive( prop_action, TRUE );
2081 } else {
2082 ege_select_one_action_set_active( flat_action, 0 );
2083 gtk_action_set_sensitive( prop_action, FALSE );
2084 }
2085 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2086 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2087 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2088 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2089 if (r2 < r1) {
2090 gtk_adjustment_set_value(adj, r2/r1);
2091 } else {
2092 gtk_adjustment_set_value(adj, r1/r2);
2093 }
2094 } else if (!strcmp(name, "sodipodi:sides")) {
2095 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2096 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2097 }
2099 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2100 }
2103 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2104 {
2105 NULL, /* child_added */
2106 NULL, /* child_removed */
2107 star_tb_event_attr_changed,
2108 NULL, /* content_changed */
2109 NULL /* order_changed */
2110 };
2113 /**
2114 * \param selection Should not be NULL.
2115 */
2116 static void
2117 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2118 {
2119 int n_selected = 0;
2120 Inkscape::XML::Node *repr = NULL;
2122 purge_repr_listener( tbl, tbl );
2124 for (GSList const *items = selection->itemList();
2125 items != NULL;
2126 items = items->next)
2127 {
2128 if (SP_IS_STAR((SPItem *) items->data)) {
2129 n_selected++;
2130 repr = SP_OBJECT_REPR((SPItem *) items->data);
2131 }
2132 }
2134 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2136 if (n_selected == 0) {
2137 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2138 } else if (n_selected == 1) {
2139 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2141 if (repr) {
2142 g_object_set_data( tbl, "repr", repr );
2143 Inkscape::GC::anchor(repr);
2144 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2145 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2146 }
2147 } else {
2148 // FIXME: implement averaging of all parameters for multiple selected stars
2149 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2150 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2151 }
2152 }
2155 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2156 {
2157 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2158 // callbacks to lump all the changes for all selected objects in one undo step
2160 GtkAdjustment *adj = 0;
2162 // fixme: make settable in prefs!
2163 gint mag = 5;
2164 gdouble prop = 0.5;
2165 gboolean flat = FALSE;
2166 gdouble randomized = 0;
2167 gdouble rounded = 0;
2169 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2170 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2172 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2173 gtk_action_set_sensitive( sb2, !flat );
2175 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2176 gtk_adjustment_set_value(adj, mag);
2177 gtk_adjustment_value_changed(adj);
2179 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2180 gtk_adjustment_set_value(adj, prop);
2181 gtk_adjustment_value_changed(adj);
2183 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2184 gtk_adjustment_set_value(adj, rounded);
2185 gtk_adjustment_value_changed(adj);
2187 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2188 gtk_adjustment_set_value(adj, randomized);
2189 gtk_adjustment_value_changed(adj);
2190 }
2193 void
2194 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2195 {
2196 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2197 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2198 GtkWidget *l = gtk_label_new(NULL);
2199 gtk_label_set_markup(GTK_LABEL(l), title);
2200 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2201 if ( GTK_IS_TOOLBAR(tbl) ) {
2202 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2203 } else {
2204 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2205 }
2206 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2207 }
2210 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2211 {
2212 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2214 {
2215 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2216 ege_output_action_set_use_markup( act, TRUE );
2217 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2218 g_object_set_data( holder, "mode_action", act );
2219 }
2221 {
2222 EgeAdjustmentAction* eact = 0;
2223 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2224 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2226 /* Flatsided checkbox */
2227 {
2228 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2230 GtkTreeIter iter;
2231 gtk_list_store_append( model, &iter );
2232 gtk_list_store_set( model, &iter,
2233 0, _("Polygon"),
2234 1, _("Regular polygon (with one handle) instead of a star"),
2235 2, "star_flat",
2236 -1 );
2238 gtk_list_store_append( model, &iter );
2239 gtk_list_store_set( model, &iter,
2240 0, _("Star"),
2241 1, _("Star instead of a regular polygon (with one handle)"),
2242 2, "star_angled",
2243 -1 );
2245 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2246 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2247 g_object_set_data( holder, "flat_action", act );
2249 ege_select_one_action_set_appearance( act, "full" );
2250 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2251 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2252 ege_select_one_action_set_icon_column( act, 2 );
2253 ege_select_one_action_set_icon_size( act, secondarySize );
2254 ege_select_one_action_set_tooltip_column( act, 1 );
2256 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2257 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2258 }
2260 /* Magnitude */
2261 {
2262 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2263 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2264 eact = create_adjustment_action( "MagnitudeAction",
2265 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2266 "tools.shapes.star", "magnitude", 3,
2267 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2268 3, 1024, 1, 5,
2269 labels, values, G_N_ELEMENTS(labels),
2270 sp_stb_magnitude_value_changed,
2271 1.0, 0 );
2272 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2273 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2274 }
2276 /* Spoke ratio */
2277 {
2278 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2279 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2280 eact = create_adjustment_action( "SpokeAction",
2281 _("Spoke ratio"), _("Spoke ratio:"),
2282 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2283 // Base radius is the same for the closest handle.
2284 _("Base radius to tip radius ratio"),
2285 "tools.shapes.star", "proportion", 0.5,
2286 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2287 0.01, 1.0, 0.01, 0.1,
2288 labels, values, G_N_ELEMENTS(labels),
2289 sp_stb_proportion_value_changed );
2290 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2291 g_object_set_data( holder, "prop_action", eact );
2292 }
2294 if ( !isFlatSided ) {
2295 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2296 } else {
2297 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2298 }
2300 /* Roundedness */
2301 {
2302 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2303 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2304 eact = create_adjustment_action( "RoundednessAction",
2305 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2306 "tools.shapes.star", "rounded", 0.0,
2307 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2308 -10.0, 10.0, 0.01, 0.1,
2309 labels, values, G_N_ELEMENTS(labels),
2310 sp_stb_rounded_value_changed );
2311 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2312 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2313 }
2315 /* Randomization */
2316 {
2317 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2318 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2319 eact = create_adjustment_action( "RandomizationAction",
2320 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2321 "tools.shapes.star", "randomized", 0.0,
2322 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2323 -10.0, 10.0, 0.001, 0.01,
2324 labels, values, G_N_ELEMENTS(labels),
2325 sp_stb_randomized_value_changed, 0.1, 3 );
2326 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2327 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2328 }
2329 }
2331 {
2332 /* Reset */
2333 {
2334 GtkAction* act = gtk_action_new( "StarResetAction",
2335 _("Defaults"),
2336 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2337 GTK_STOCK_CLEAR );
2338 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2339 gtk_action_group_add_action( mainActions, act );
2340 gtk_action_set_sensitive( act, TRUE );
2341 }
2342 }
2344 sigc::connection *connection = new sigc::connection(
2345 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2346 );
2347 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2348 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2349 }
2352 //########################
2353 //## Rect ##
2354 //########################
2356 static void sp_rtb_sensitivize( GObject *tbl )
2357 {
2358 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2359 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2360 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2362 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2363 gtk_action_set_sensitive( not_rounded, FALSE );
2364 } else {
2365 gtk_action_set_sensitive( not_rounded, TRUE );
2366 }
2367 }
2370 static void
2371 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2372 void (*setter)(SPRect *, gdouble))
2373 {
2374 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2376 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2377 SPUnit const *unit = tracker->getActiveUnit();
2379 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2380 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2381 }
2383 // quit if run by the attr_changed listener
2384 if (g_object_get_data( tbl, "freeze" )) {
2385 return;
2386 }
2388 // in turn, prevent listener from responding
2389 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2391 bool modmade = false;
2392 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2393 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2394 if (SP_IS_RECT(items->data)) {
2395 if (adj->value != 0) {
2396 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2397 } else {
2398 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2399 }
2400 modmade = true;
2401 }
2402 }
2404 sp_rtb_sensitivize( tbl );
2406 if (modmade) {
2407 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2408 _("Change rectangle"));
2409 }
2411 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2412 }
2414 static void
2415 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2416 {
2417 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2418 }
2420 static void
2421 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2422 {
2423 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2424 }
2426 static void
2427 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2428 {
2429 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2430 }
2432 static void
2433 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2434 {
2435 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2436 }
2440 static void
2441 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2442 {
2443 GtkAdjustment *adj = 0;
2445 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2446 gtk_adjustment_set_value(adj, 0.0);
2447 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2448 gtk_adjustment_value_changed(adj);
2450 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2451 gtk_adjustment_set_value(adj, 0.0);
2452 gtk_adjustment_value_changed(adj);
2454 sp_rtb_sensitivize( obj );
2455 }
2457 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2458 gchar const */*old_value*/, gchar const */*new_value*/,
2459 bool /*is_interactive*/, gpointer data)
2460 {
2461 GObject *tbl = G_OBJECT(data);
2463 // quit if run by the _changed callbacks
2464 if (g_object_get_data( tbl, "freeze" )) {
2465 return;
2466 }
2468 // in turn, prevent callbacks from responding
2469 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2471 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2472 SPUnit const *unit = tracker->getActiveUnit();
2474 gpointer item = g_object_get_data( tbl, "item" );
2475 if (item && SP_IS_RECT(item)) {
2476 {
2477 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2478 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2479 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2480 }
2482 {
2483 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2484 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2485 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2486 }
2488 {
2489 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2490 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2491 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2492 }
2494 {
2495 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2496 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2497 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2498 }
2499 }
2501 sp_rtb_sensitivize( tbl );
2503 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2504 }
2507 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2508 NULL, /* child_added */
2509 NULL, /* child_removed */
2510 rect_tb_event_attr_changed,
2511 NULL, /* content_changed */
2512 NULL /* order_changed */
2513 };
2515 /**
2516 * \param selection should not be NULL.
2517 */
2518 static void
2519 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2520 {
2521 int n_selected = 0;
2522 Inkscape::XML::Node *repr = NULL;
2523 SPItem *item = NULL;
2525 if ( g_object_get_data( tbl, "repr" ) ) {
2526 g_object_set_data( tbl, "item", NULL );
2527 }
2528 purge_repr_listener( tbl, tbl );
2530 for (GSList const *items = selection->itemList();
2531 items != NULL;
2532 items = items->next) {
2533 if (SP_IS_RECT((SPItem *) items->data)) {
2534 n_selected++;
2535 item = (SPItem *) items->data;
2536 repr = SP_OBJECT_REPR(item);
2537 }
2538 }
2540 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2542 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2544 if (n_selected == 0) {
2545 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2547 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2548 gtk_action_set_sensitive(w, FALSE);
2549 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2550 gtk_action_set_sensitive(h, FALSE);
2552 } else if (n_selected == 1) {
2553 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2554 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2556 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2557 gtk_action_set_sensitive(w, TRUE);
2558 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2559 gtk_action_set_sensitive(h, TRUE);
2561 if (repr) {
2562 g_object_set_data( tbl, "repr", repr );
2563 g_object_set_data( tbl, "item", item );
2564 Inkscape::GC::anchor(repr);
2565 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2566 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2567 }
2568 } else {
2569 // FIXME: implement averaging of all parameters for multiple selected
2570 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2571 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2572 sp_rtb_sensitivize( tbl );
2573 }
2574 }
2577 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2578 {
2579 EgeAdjustmentAction* eact = 0;
2580 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2582 {
2583 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2584 ege_output_action_set_use_markup( act, TRUE );
2585 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2586 g_object_set_data( holder, "mode_action", act );
2587 }
2589 // rx/ry units menu: create
2590 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2591 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2592 // fixme: add % meaning per cent of the width/height
2593 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2594 g_object_set_data( holder, "tracker", tracker );
2596 /* W */
2597 {
2598 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2599 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2600 eact = create_adjustment_action( "RectWidthAction",
2601 _("Width"), _("W:"), _("Width of rectangle"),
2602 "tools.shapes.rect", "width", 0,
2603 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2604 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2605 labels, values, G_N_ELEMENTS(labels),
2606 sp_rtb_width_value_changed );
2607 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2608 g_object_set_data( holder, "width_action", eact );
2609 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2610 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2611 }
2613 /* H */
2614 {
2615 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2616 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2617 eact = create_adjustment_action( "RectHeightAction",
2618 _("Height"), _("H:"), _("Height of rectangle"),
2619 "tools.shapes.rect", "height", 0,
2620 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2621 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2622 labels, values, G_N_ELEMENTS(labels),
2623 sp_rtb_height_value_changed );
2624 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2625 g_object_set_data( holder, "height_action", eact );
2626 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2627 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2628 }
2630 /* rx */
2631 {
2632 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2633 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2634 eact = create_adjustment_action( "RadiusXAction",
2635 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2636 "tools.shapes.rect", "rx", 0,
2637 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2638 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2639 labels, values, G_N_ELEMENTS(labels),
2640 sp_rtb_rx_value_changed);
2641 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2642 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2643 }
2645 /* ry */
2646 {
2647 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2648 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2649 eact = create_adjustment_action( "RadiusYAction",
2650 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2651 "tools.shapes.rect", "ry", 0,
2652 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2653 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2654 labels, values, G_N_ELEMENTS(labels),
2655 sp_rtb_ry_value_changed);
2656 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2657 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2658 }
2660 // add the units menu
2661 {
2662 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2663 gtk_action_group_add_action( mainActions, act );
2664 }
2666 /* Reset */
2667 {
2668 InkAction* inky = ink_action_new( "RectResetAction",
2669 _("Not rounded"),
2670 _("Make corners sharp"),
2671 "squared_corner",
2672 secondarySize );
2673 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2674 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2675 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2676 g_object_set_data( holder, "not_rounded", inky );
2677 }
2679 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2680 sp_rtb_sensitivize( holder );
2682 sigc::connection *connection = new sigc::connection(
2683 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2684 );
2685 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2686 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2687 }
2689 //########################
2690 //## 3D Box ##
2691 //########################
2693 // normalize angle so that it lies in the interval [0,360]
2694 static double box3d_normalize_angle (double a) {
2695 double angle = a + ((int) (a/360.0))*360;
2696 if (angle < 0) {
2697 angle += 360.0;
2698 }
2699 return angle;
2700 }
2702 static void
2703 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2704 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2705 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2706 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2707 // are reset).
2708 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2710 if (is_infinite) {
2711 gtk_toggle_action_set_active(tact, TRUE);
2712 gtk_action_set_sensitive(act, TRUE);
2714 double angle = persp3d_get_infinite_angle(persp, axis);
2715 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2716 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2717 }
2718 } else {
2719 gtk_toggle_action_set_active(tact, FALSE);
2720 gtk_action_set_sensitive(act, FALSE);
2721 }
2722 }
2724 static void
2725 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2726 if (!persp_repr) {
2727 g_print ("No perspective given to box3d_resync_toolbar().\n");
2728 return;
2729 }
2731 GtkWidget *tbl = GTK_WIDGET(data);
2732 GtkAdjustment *adj = 0;
2733 GtkAction *act = 0;
2734 GtkToggleAction *tact = 0;
2735 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2736 {
2737 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2738 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2739 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2741 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2742 }
2743 {
2744 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2745 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2746 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2748 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2749 }
2750 {
2751 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2752 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2753 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2755 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2756 }
2757 }
2759 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2760 gchar const */*old_value*/, gchar const */*new_value*/,
2761 bool /*is_interactive*/, gpointer data)
2762 {
2763 GtkWidget *tbl = GTK_WIDGET(data);
2765 // quit if run by the attr_changed listener
2766 // note: it used to work without the differently called freeze_ attributes (here and in
2767 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2768 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2769 return;
2770 }
2772 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2773 // sp_document_maybe_done() when the document is undo insensitive)
2774 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2776 // TODO: Only update the appropriate part of the toolbar
2777 // if (!strcmp(name, "inkscape:vp_z")) {
2778 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2779 // }
2781 Persp3D *persp = persp3d_get_from_repr(repr);
2782 persp3d_update_box_reprs(persp);
2784 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2785 }
2787 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2788 {
2789 NULL, /* child_added */
2790 NULL, /* child_removed */
2791 box3d_persp_tb_event_attr_changed,
2792 NULL, /* content_changed */
2793 NULL /* order_changed */
2794 };
2796 /**
2797 * \param selection Should not be NULL.
2798 */
2799 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2800 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2801 static void
2802 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2803 {
2804 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2805 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2806 // update the perspectives with infinite VPs and leave the other ones untouched).
2808 Inkscape::XML::Node *persp_repr = NULL;
2809 purge_repr_listener(tbl, tbl);
2811 SPItem *item = selection->singleItem();
2812 if (item && SP_IS_BOX3D(item)) {
2813 // FIXME: Also deal with multiple selected boxes
2814 SPBox3D *box = SP_BOX3D(item);
2815 Persp3D *persp = box3d_get_perspective(box);
2816 persp_repr = SP_OBJECT_REPR(persp);
2817 if (persp_repr) {
2818 g_object_set_data(tbl, "repr", persp_repr);
2819 Inkscape::GC::anchor(persp_repr);
2820 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2821 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2822 }
2824 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2825 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2827 box3d_resync_toolbar(persp_repr, tbl);
2828 }
2829 }
2831 static void
2832 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2833 {
2834 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2835 SPDocument *document = sp_desktop_document(desktop);
2837 // quit if run by the attr_changed listener
2838 // note: it used to work without the differently called freeze_ attributes (here and in
2839 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2840 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2841 return;
2842 }
2844 // in turn, prevent listener from responding
2845 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2847 //Persp3D *persp = document->current_persp3d;
2848 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2849 if (sel_persps.empty()) {
2850 // this can happen when the document is created; we silently ignore it
2851 return;
2852 }
2853 Persp3D *persp = sel_persps.front();
2855 persp->tmat.set_infinite_direction (axis, adj->value);
2856 SP_OBJECT(persp)->updateRepr();
2858 // TODO: use the correct axis here, too
2859 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2861 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2862 }
2865 static void
2866 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2867 {
2868 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2869 }
2871 static void
2872 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2873 {
2874 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2875 }
2877 static void
2878 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2879 {
2880 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2881 }
2884 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2885 {
2886 // TODO: Take all selected perspectives into account
2887 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2888 if (sel_persps.empty()) {
2889 // this can happen when the document is created; we silently ignore it
2890 return;
2891 }
2892 Persp3D *persp = sel_persps.front();
2894 bool set_infinite = gtk_toggle_action_get_active(act);
2895 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2896 }
2898 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2899 {
2900 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2901 }
2903 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2904 {
2905 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2906 }
2908 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2909 {
2910 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2911 }
2913 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2914 {
2915 EgeAdjustmentAction* eact = 0;
2916 SPDocument *document = sp_desktop_document (desktop);
2917 Persp3D *persp = document->current_persp3d;
2919 EgeAdjustmentAction* box3d_angle_x = 0;
2920 EgeAdjustmentAction* box3d_angle_y = 0;
2921 EgeAdjustmentAction* box3d_angle_z = 0;
2923 /* Angle X */
2924 {
2925 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2926 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2927 eact = create_adjustment_action( "3DBoxAngleXAction",
2928 _("Angle in X direction"), _("Angle X:"),
2929 // Translators: PL is short for 'perspective line'
2930 _("Angle of PLs in X direction"),
2931 "tools.shapes.3dbox", "box3d_angle_x", 30,
2932 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2933 -360.0, 360.0, 1.0, 10.0,
2934 labels, values, G_N_ELEMENTS(labels),
2935 box3d_angle_x_value_changed );
2936 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2937 g_object_set_data( holder, "box3d_angle_x_action", eact );
2938 box3d_angle_x = eact;
2939 }
2941 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2942 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2943 } else {
2944 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2945 }
2948 /* VP X state */
2949 {
2950 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2951 // Translators: VP is short for 'vanishing point'
2952 _("State of VP in X direction"),
2953 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2954 "toggle_vp_x",
2955 Inkscape::ICON_SIZE_DECORATION );
2956 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2957 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2958 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2959 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2960 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2961 }
2963 /* Angle Y */
2964 {
2965 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2966 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2967 eact = create_adjustment_action( "3DBoxAngleYAction",
2968 _("Angle in Y direction"), _("Angle Y:"),
2969 // Translators: PL is short for 'perspective line'
2970 _("Angle of PLs in Y direction"),
2971 "tools.shapes.3dbox", "box3d_angle_y", 30,
2972 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2973 -360.0, 360.0, 1.0, 10.0,
2974 labels, values, G_N_ELEMENTS(labels),
2975 box3d_angle_y_value_changed );
2976 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2977 g_object_set_data( holder, "box3d_angle_y_action", eact );
2978 box3d_angle_y = eact;
2979 }
2981 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2982 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2983 } else {
2984 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2985 }
2987 /* VP Y state */
2988 {
2989 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2990 // Translators: VP is short for 'vanishing point'
2991 _("State of VP in Y direction"),
2992 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2993 "toggle_vp_y",
2994 Inkscape::ICON_SIZE_DECORATION );
2995 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2996 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2997 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2998 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2999 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
3000 }
3002 /* Angle Z */
3003 {
3004 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3005 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3006 eact = create_adjustment_action( "3DBoxAngleZAction",
3007 _("Angle in Z direction"), _("Angle Z:"),
3008 // Translators: PL is short for 'perspective line'
3009 _("Angle of PLs in Z direction"),
3010 "tools.shapes.3dbox", "box3d_angle_z", 30,
3011 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3012 -360.0, 360.0, 1.0, 10.0,
3013 labels, values, G_N_ELEMENTS(labels),
3014 box3d_angle_z_value_changed );
3015 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3016 g_object_set_data( holder, "box3d_angle_z_action", eact );
3017 box3d_angle_z = eact;
3018 }
3020 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3021 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3022 } else {
3023 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3024 }
3026 /* VP Z state */
3027 {
3028 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3029 // Translators: VP is short for 'vanishing point'
3030 _("State of VP in Z direction"),
3031 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3032 "toggle_vp_z",
3033 Inkscape::ICON_SIZE_DECORATION );
3034 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3035 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3036 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3037 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3038 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3039 }
3041 sigc::connection *connection = new sigc::connection(
3042 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3043 );
3044 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3045 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3046 }
3048 //########################
3049 //## Spiral ##
3050 //########################
3052 static void
3053 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3054 {
3055 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3057 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3058 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3059 }
3061 // quit if run by the attr_changed listener
3062 if (g_object_get_data( tbl, "freeze" )) {
3063 return;
3064 }
3066 // in turn, prevent listener from responding
3067 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3069 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3071 bool modmade = false;
3072 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3073 items != NULL;
3074 items = items->next)
3075 {
3076 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3077 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3078 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3079 SP_OBJECT((SPItem *) items->data)->updateRepr();
3080 modmade = true;
3081 }
3082 }
3084 g_free(namespaced_name);
3086 if (modmade) {
3087 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3088 _("Change spiral"));
3089 }
3091 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3092 }
3094 static void
3095 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3096 {
3097 sp_spl_tb_value_changed(adj, tbl, "revolution");
3098 }
3100 static void
3101 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3102 {
3103 sp_spl_tb_value_changed(adj, tbl, "expansion");
3104 }
3106 static void
3107 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3108 {
3109 sp_spl_tb_value_changed(adj, tbl, "t0");
3110 }
3112 static void
3113 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3114 {
3115 GtkWidget *tbl = GTK_WIDGET(obj);
3117 GtkAdjustment *adj;
3119 // fixme: make settable
3120 gdouble rev = 5;
3121 gdouble exp = 1.0;
3122 gdouble t0 = 0.0;
3124 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3125 gtk_adjustment_set_value(adj, rev);
3126 gtk_adjustment_value_changed(adj);
3128 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3129 gtk_adjustment_set_value(adj, exp);
3130 gtk_adjustment_value_changed(adj);
3132 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3133 gtk_adjustment_set_value(adj, t0);
3134 gtk_adjustment_value_changed(adj);
3136 spinbutton_defocus(GTK_OBJECT(tbl));
3137 }
3140 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3141 gchar const */*old_value*/, gchar const */*new_value*/,
3142 bool /*is_interactive*/, gpointer data)
3143 {
3144 GtkWidget *tbl = GTK_WIDGET(data);
3146 // quit if run by the _changed callbacks
3147 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3148 return;
3149 }
3151 // in turn, prevent callbacks from responding
3152 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3154 GtkAdjustment *adj;
3155 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3156 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3158 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3159 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3161 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3162 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3164 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3165 }
3168 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3169 NULL, /* child_added */
3170 NULL, /* child_removed */
3171 spiral_tb_event_attr_changed,
3172 NULL, /* content_changed */
3173 NULL /* order_changed */
3174 };
3176 static void
3177 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3178 {
3179 int n_selected = 0;
3180 Inkscape::XML::Node *repr = NULL;
3182 purge_repr_listener( tbl, tbl );
3184 for (GSList const *items = selection->itemList();
3185 items != NULL;
3186 items = items->next)
3187 {
3188 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3189 n_selected++;
3190 repr = SP_OBJECT_REPR((SPItem *) items->data);
3191 }
3192 }
3194 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3196 if (n_selected == 0) {
3197 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3198 } else if (n_selected == 1) {
3199 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3201 if (repr) {
3202 g_object_set_data( tbl, "repr", repr );
3203 Inkscape::GC::anchor(repr);
3204 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3205 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3206 }
3207 } else {
3208 // FIXME: implement averaging of all parameters for multiple selected
3209 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3210 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3211 }
3212 }
3215 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3216 {
3217 EgeAdjustmentAction* eact = 0;
3218 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3220 {
3221 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3222 ege_output_action_set_use_markup( act, TRUE );
3223 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3224 g_object_set_data( holder, "mode_action", act );
3225 }
3227 /* Revolution */
3228 {
3229 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3230 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3231 eact = create_adjustment_action( "SpiralRevolutionAction",
3232 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3233 "tools.shapes.spiral", "revolution", 3.0,
3234 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3235 0.01, 1024.0, 0.1, 1.0,
3236 labels, values, G_N_ELEMENTS(labels),
3237 sp_spl_tb_revolution_value_changed, 1, 2);
3238 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3239 }
3241 /* Expansion */
3242 {
3243 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3244 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3245 eact = create_adjustment_action( "SpiralExpansionAction",
3246 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3247 "tools.shapes.spiral", "expansion", 1.0,
3248 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3249 0.0, 1000.0, 0.01, 1.0,
3250 labels, values, G_N_ELEMENTS(labels),
3251 sp_spl_tb_expansion_value_changed);
3252 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3253 }
3255 /* T0 */
3256 {
3257 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3258 gdouble values[] = {0, 0.5, 0.9};
3259 eact = create_adjustment_action( "SpiralT0Action",
3260 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3261 "tools.shapes.spiral", "t0", 0.0,
3262 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3263 0.0, 0.999, 0.01, 1.0,
3264 labels, values, G_N_ELEMENTS(labels),
3265 sp_spl_tb_t0_value_changed);
3266 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3267 }
3269 /* Reset */
3270 {
3271 InkAction* inky = ink_action_new( "SpiralResetAction",
3272 _("Defaults"),
3273 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3274 GTK_STOCK_CLEAR,
3275 secondarySize );
3276 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3277 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3278 }
3281 sigc::connection *connection = new sigc::connection(
3282 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3283 );
3284 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3285 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3286 }
3288 //########################
3289 //## Pen/Pencil ##
3290 //########################
3292 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3293 static char const *
3294 freehand_tool_name(GObject *dataKludge)
3295 {
3296 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3297 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3298 ? "tools.freehand.pen"
3299 : "tools.freehand.pencil" );
3300 }
3302 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3303 {
3304 gint mode = ege_select_one_action_get_active(act);
3306 prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3308 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3310 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3311 // preparatory work here
3312 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3313 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3314 sp_pen_context_set_polyline_mode(pc);
3315 }
3316 }
3318 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3319 {
3320 /* Freehand mode toggle buttons */
3321 {
3322 guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3323 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3325 {
3326 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3328 GtkTreeIter iter;
3329 gtk_list_store_append( model, &iter );
3330 gtk_list_store_set( model, &iter,
3331 0, _("Bezier"),
3332 1, _("Create regular Bezier path"),
3333 2, "bezier_mode",
3334 -1 );
3336 gtk_list_store_append( model, &iter );
3337 gtk_list_store_set( model, &iter,
3338 0, _("Spiro"),
3339 1, _("Create Spiro path"),
3340 2, "spiro_splines_mode",
3341 -1 );
3343 if (!tool_is_pencil) {
3344 gtk_list_store_append( model, &iter );
3345 gtk_list_store_set( model, &iter,
3346 0, _("Zigzag"),
3347 1, _("Create a sequence of straight line segments"),
3348 2, "polylines_mode",
3349 -1 );
3351 gtk_list_store_append( model, &iter );
3352 gtk_list_store_set( model, &iter,
3353 0, _("Paraxial"),
3354 1, _("Create a sequence of paraxial line segments"),
3355 2, "paraxial_lines_mode",
3356 -1 );
3357 }
3359 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3360 "FreehandModeActionPencil" :
3361 "FreehandModeActionPen",
3362 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3363 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3365 ege_select_one_action_set_appearance( act, "full" );
3366 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3367 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3368 ege_select_one_action_set_icon_column( act, 2 );
3369 ege_select_one_action_set_icon_size( act, secondarySize );
3370 ege_select_one_action_set_tooltip_column( act, 1 );
3372 ege_select_one_action_set_active( act, freehandMode);
3373 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3374 }
3375 }
3376 }
3378 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3379 gint shape = ege_select_one_action_get_active( act );
3380 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3381 }
3383 /**
3384 * \brief Generate the list of freehand advanced shape option entries.
3385 */
3386 GList * freehand_shape_dropdown_items_list() {
3387 GList *glist = NULL;
3389 glist = g_list_append (glist, _("None"));
3390 glist = g_list_append (glist, _("Triangle in"));
3391 glist = g_list_append (glist, _("Triangle out"));
3392 glist = g_list_append (glist, _("Ellipse"));
3393 glist = g_list_append (glist, _("From clipboard"));
3395 return glist;
3396 }
3398 static void
3399 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3400 /*advanced shape options */
3401 {
3402 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3404 GList* items = 0;
3405 gint count = 0;
3406 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3407 {
3408 GtkTreeIter iter;
3409 gtk_list_store_append( model, &iter );
3410 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3411 count++;
3412 }
3413 g_list_free( items );
3414 items = 0;
3415 EgeSelectOneAction* act1 = ege_select_one_action_new(
3416 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3417 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3418 g_object_set( act1, "short_label", _("Shape:"), NULL );
3419 ege_select_one_action_set_appearance( act1, "compact" );
3420 ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3421 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3422 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3423 g_object_set_data( holder, "shape_action", act1 );
3424 }
3425 }
3427 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3428 {
3429 sp_add_freehand_mode_toggle(mainActions, holder, false);
3430 freehand_add_advanced_shape_options(mainActions, holder, false);
3431 }
3434 static void
3435 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3436 {
3437 GtkWidget *tbl = GTK_WIDGET(obj);
3439 GtkAdjustment *adj;
3441 // fixme: make settable
3442 gdouble tolerance = 4;
3444 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3445 gtk_adjustment_set_value(adj, tolerance);
3446 gtk_adjustment_value_changed(adj);
3448 spinbutton_defocus(GTK_OBJECT(tbl));
3449 }
3451 static void
3452 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3453 {
3455 // quit if run by the attr_changed listener
3456 if (g_object_get_data( tbl, "freeze" )) {
3457 return;
3458 }
3459 // in turn, prevent listener from responding
3460 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3461 prefs_set_double_attribute("tools.freehand.pencil",
3462 "tolerance", adj->value);
3463 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3465 }
3469 static void
3470 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3471 const gchar */*key*/,
3472 const gchar */*oldval*/,
3473 const gchar */*newval*/,
3474 bool /*is_interactive*/,
3475 void * data)
3476 {
3477 GObject* tbl = G_OBJECT(data);
3478 if (g_object_get_data( tbl, "freeze" )) {
3479 return;
3480 }
3482 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3484 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3485 "tolerance");
3487 double v = prefs_get_double_attribute("tools.freehand.pencil",
3488 "tolerance", adj->value);
3489 gtk_adjustment_set_value(adj, v);
3490 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3492 }
3494 static Inkscape::XML::NodeEventVector pencil_node_events =
3495 {
3496 NULL,
3497 NULL,
3498 sp_pencil_tb_tolerance_value_changed_external,
3499 NULL,
3500 NULL,
3501 };
3504 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3505 {
3506 sp_add_freehand_mode_toggle(mainActions, holder, true);
3508 EgeAdjustmentAction* eact = 0;
3510 /* Tolerance */
3511 {
3512 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3513 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3514 eact = create_adjustment_action( "PencilToleranceAction",
3515 _("Smoothing:"), _("Smoothing: "),
3516 _("How much smoothing (simplifying) is applied to the line"),
3517 "tools.freehand.pencil", "tolerance",
3518 3.0,
3519 GTK_WIDGET(desktop->canvas), NULL,
3520 holder, TRUE, "altx-pencil",
3521 1, 100.0, 0.5, 0,
3522 labels, values, G_N_ELEMENTS(labels),
3523 sp_pencil_tb_tolerance_value_changed,
3524 1, 2);
3525 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3526 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3528 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3529 "tools.freehand.pencil");
3530 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3531 g_object_set_data(G_OBJECT(holder), "repr", repr);
3533 }
3535 /* advanced shape options */
3536 freehand_add_advanced_shape_options(mainActions, holder, true);
3538 /* Reset */
3539 {
3540 InkAction* inky = ink_action_new( "PencilResetAction",
3541 _("Defaults"),
3542 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3543 GTK_STOCK_CLEAR,
3544 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3545 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3546 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3547 }
3549 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3551 }
3554 //########################
3555 //## Tweak ##
3556 //########################
3558 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3559 {
3560 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3561 }
3563 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3564 {
3565 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3566 }
3568 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3569 {
3570 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3571 }
3573 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3574 {
3575 int mode = ege_select_one_action_get_active( act );
3576 prefs_set_int_attribute("tools.tweak", "mode", mode);
3578 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3579 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3580 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3581 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3582 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3583 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3584 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3585 if (doh) gtk_action_set_sensitive (doh, TRUE);
3586 if (dos) gtk_action_set_sensitive (dos, TRUE);
3587 if (dol) gtk_action_set_sensitive (dol, TRUE);
3588 if (doo) gtk_action_set_sensitive (doo, TRUE);
3589 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3590 if (fid) gtk_action_set_sensitive (fid, FALSE);
3591 } else {
3592 if (doh) gtk_action_set_sensitive (doh, FALSE);
3593 if (dos) gtk_action_set_sensitive (dos, FALSE);
3594 if (dol) gtk_action_set_sensitive (dol, FALSE);
3595 if (doo) gtk_action_set_sensitive (doo, FALSE);
3596 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3597 if (fid) gtk_action_set_sensitive (fid, TRUE);
3598 }
3599 }
3601 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3602 {
3603 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3604 }
3606 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3607 bool show = gtk_toggle_action_get_active( act );
3608 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3609 }
3610 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3611 bool show = gtk_toggle_action_get_active( act );
3612 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3613 }
3614 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3615 bool show = gtk_toggle_action_get_active( act );
3616 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3617 }
3618 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3619 bool show = gtk_toggle_action_get_active( act );
3620 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3621 }
3623 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3624 {
3625 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3627 {
3628 /* Width */
3629 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3630 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3631 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3632 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3633 "tools.tweak", "width", 15,
3634 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3635 1, 100, 1.0, 0.0,
3636 labels, values, G_N_ELEMENTS(labels),
3637 sp_tweak_width_value_changed, 0.01, 0, 100 );
3638 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3639 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3640 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3641 }
3644 {
3645 /* Force */
3646 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3647 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3648 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3649 _("Force"), _("Force:"), _("The force of the tweak action"),
3650 "tools.tweak", "force", 20,
3651 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3652 1, 100, 1.0, 0.0,
3653 labels, values, G_N_ELEMENTS(labels),
3654 sp_tweak_force_value_changed, 0.01, 0, 100 );
3655 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3656 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3657 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3658 }
3660 /* Mode */
3661 {
3662 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3664 GtkTreeIter iter;
3665 gtk_list_store_append( model, &iter );
3666 gtk_list_store_set( model, &iter,
3667 0, _("Push mode"),
3668 1, _("Push parts of paths in any direction"),
3669 2, "tweak_push_mode",
3670 -1 );
3672 gtk_list_store_append( model, &iter );
3673 gtk_list_store_set( model, &iter,
3674 0, _("Shrink mode"),
3675 1, _("Shrink (inset) parts of paths"),
3676 2, "tweak_shrink_mode",
3677 -1 );
3679 gtk_list_store_append( model, &iter );
3680 gtk_list_store_set( model, &iter,
3681 0, _("Grow mode"),
3682 1, _("Grow (outset) parts of paths"),
3683 2, "tweak_grow_mode",
3684 -1 );
3686 gtk_list_store_append( model, &iter );
3687 gtk_list_store_set( model, &iter,
3688 0, _("Attract mode"),
3689 1, _("Attract parts of paths towards cursor"),
3690 2, "tweak_attract_mode",
3691 -1 );
3693 gtk_list_store_append( model, &iter );
3694 gtk_list_store_set( model, &iter,
3695 0, _("Repel mode"),
3696 1, _("Repel parts of paths from cursor"),
3697 2, "tweak_repel_mode",
3698 -1 );
3700 gtk_list_store_append( model, &iter );
3701 gtk_list_store_set( model, &iter,
3702 0, _("Roughen mode"),
3703 1, _("Roughen parts of paths"),
3704 2, "tweak_roughen_mode",
3705 -1 );
3707 gtk_list_store_append( model, &iter );
3708 gtk_list_store_set( model, &iter,
3709 0, _("Color paint mode"),
3710 1, _("Paint the tool's color upon selected objects"),
3711 2, "tweak_colorpaint_mode",
3712 -1 );
3714 gtk_list_store_append( model, &iter );
3715 gtk_list_store_set( model, &iter,
3716 0, _("Color jitter mode"),
3717 1, _("Jitter the colors of selected objects"),
3718 2, "tweak_colorjitter_mode",
3719 -1 );
3721 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3722 g_object_set( act, "short_label", _("Mode:"), NULL );
3723 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3724 g_object_set_data( holder, "mode_action", act );
3726 ege_select_one_action_set_appearance( act, "full" );
3727 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3728 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3729 ege_select_one_action_set_icon_column( act, 2 );
3730 ege_select_one_action_set_icon_size( act, secondarySize );
3731 ege_select_one_action_set_tooltip_column( act, 1 );
3733 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3734 ege_select_one_action_set_active( act, mode );
3735 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3737 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3738 }
3740 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3742 {
3743 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3744 ege_output_action_set_use_markup( act, TRUE );
3745 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3746 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3747 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3748 g_object_set_data( holder, "tweak_channels_label", act);
3749 }
3751 {
3752 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3753 _("Hue"),
3754 _("In color mode, act on objects' hue"),
3755 NULL,
3756 Inkscape::ICON_SIZE_DECORATION );
3757 //TRANSLATORS: "H" here stands for hue
3758 g_object_set( act, "short_label", _("H"), NULL );
3759 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3760 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3761 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3762 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3763 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3764 g_object_set_data( holder, "tweak_doh", act);
3765 }
3766 {
3767 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3768 _("Saturation"),
3769 _("In color mode, act on objects' saturation"),
3770 NULL,
3771 Inkscape::ICON_SIZE_DECORATION );
3772 //TRANSLATORS: "S" here stands for Saturation
3773 g_object_set( act, "short_label", _("S"), NULL );
3774 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3775 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3776 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3777 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3778 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3779 g_object_set_data( holder, "tweak_dos", act );
3780 }
3781 {
3782 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3783 _("Lightness"),
3784 _("In color mode, act on objects' lightness"),
3785 NULL,
3786 Inkscape::ICON_SIZE_DECORATION );
3787 //TRANSLATORS: "L" here stands for Lightness
3788 g_object_set( act, "short_label", _("L"), NULL );
3789 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3790 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3791 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3792 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3793 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3794 g_object_set_data( holder, "tweak_dol", act );
3795 }
3796 {
3797 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3798 _("Opacity"),
3799 _("In color mode, act on objects' opacity"),
3800 NULL,
3801 Inkscape::ICON_SIZE_DECORATION );
3802 //TRANSLATORS: "O" here stands for Opacity
3803 g_object_set( act, "short_label", _("O"), NULL );
3804 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3805 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3806 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3807 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3808 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3809 g_object_set_data( holder, "tweak_doo", act );
3810 }
3812 { /* Fidelity */
3813 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3814 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3815 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3816 _("Fidelity"), _("Fidelity:"),
3817 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3818 "tools.tweak", "fidelity", 50,
3819 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3820 1, 100, 1.0, 10.0,
3821 labels, values, G_N_ELEMENTS(labels),
3822 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3823 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3824 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3825 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3826 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3827 g_object_set_data( holder, "tweak_fidelity", eact );
3828 }
3831 /* Use Pressure button */
3832 {
3833 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3834 _("Pressure"),
3835 _("Use the pressure of the input device to alter the force of tweak action"),
3836 "use_pressure",
3837 Inkscape::ICON_SIZE_DECORATION );
3838 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3839 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3840 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3841 }
3843 }
3846 //########################
3847 //## Calligraphy ##
3848 //########################
3849 static void update_presets_list (GObject *tbl)
3850 {
3851 if (g_object_get_data(tbl, "presets_blocked"))
3852 return;
3854 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3855 if (!sel) {
3856 ege_select_one_action_set_active(sel, 0);
3857 return;
3858 }
3860 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3862 for (int i = 1; i <= total_prefs; i++) {
3863 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3864 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3866 bool match = true;
3868 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3869 iter;
3870 ++iter ) {
3871 const gchar *attr_name = g_quark_to_string(iter->key);
3872 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3873 continue;
3874 void *widget = g_object_get_data(tbl, attr_name);
3875 if (widget) {
3876 if (GTK_IS_ADJUSTMENT(widget)) {
3877 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3878 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3879 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3880 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3881 match = false;
3882 break;
3883 }
3884 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3885 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3886 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3887 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3888 if (gtk_toggle_action_get_active(toggle) != v) {
3889 match = false;
3890 break;
3891 }
3892 }
3893 }
3894 }
3896 if (match) {
3897 // newly added item is at the same index as the
3898 // save command, so we need to change twice for it to take effect
3899 ege_select_one_action_set_active(sel, 0);
3900 ege_select_one_action_set_active(sel, i);
3901 return;
3902 }
3903 }
3905 // no match found
3906 ege_select_one_action_set_active(sel, 0);
3907 }
3909 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3910 {
3911 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3912 update_presets_list(tbl);
3913 }
3915 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3916 {
3917 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3918 update_presets_list(tbl);
3919 }
3921 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3922 {
3923 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3924 update_presets_list(tbl);
3925 }
3927 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3928 {
3929 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3930 update_presets_list(tbl);
3931 }
3933 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3934 {
3935 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3936 update_presets_list(tbl);
3937 }
3939 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3940 {
3941 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3942 update_presets_list(tbl);
3943 }
3945 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3946 {
3947 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3948 update_presets_list(tbl);
3949 }
3951 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3952 {
3953 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3954 update_presets_list(tbl);
3955 }
3957 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3958 {
3959 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3960 update_presets_list(tbl);
3961 }
3963 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3964 {
3965 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3966 update_presets_list(tbl);
3967 }
3969 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3970 {
3971 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3972 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3973 update_presets_list(tbl);
3974 if (calligraphy_angle )
3975 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3976 }
3979 static gchar const *const widget_names[] = {
3980 "width",
3981 "mass",
3982 "wiggle",
3983 "angle",
3984 "thinning",
3985 "tremor",
3986 "flatness",
3987 "cap_rounding",
3988 "usepressure",
3989 "tracebackground",
3990 "usetilt"
3991 };
3994 static void sp_dcc_build_presets_list(GObject *tbl)
3995 {
3996 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3998 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3999 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4000 gtk_list_store_clear (model);
4002 {
4003 GtkTreeIter iter;
4004 gtk_list_store_append( model, &iter );
4005 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4006 }
4008 //TODO: switch back to prefs API
4009 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4010 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4011 int ii=1;
4012 while (child_repr) {
4013 GtkTreeIter iter;
4014 char *preset_name = (char *) child_repr->attribute("name");
4015 gtk_list_store_append( model, &iter );
4016 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4017 child_repr = sp_repr_next(child_repr);
4018 }
4020 {
4021 GtkTreeIter iter;
4022 gtk_list_store_append( model, &iter );
4023 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4024 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4025 }
4027 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4029 update_presets_list (tbl);
4030 }
4032 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4033 {
4034 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4035 if (! desktop) return;
4037 if (g_object_get_data(tbl, "presets_blocked"))
4038 return;
4040 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4041 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4042 // dialog cancelled
4043 update_presets_list (tbl);
4044 return;
4045 }
4046 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4048 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4049 // empty name entered
4050 update_presets_list (tbl);
4051 return;
4052 }
4054 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4056 int new_index = -1;
4057 gchar *pref_path = NULL;
4058 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4060 for (int i = 1; i <= total_prefs; i++) {
4061 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4062 const gchar *name = prefs_get_string_attribute(path, "name");
4063 if (name && !strcmp(name, profile_name.c_str())) {
4064 // we already have preset with this name, replace its values
4065 new_index = i;
4066 pref_path = g_strdup(path);
4067 break;
4068 }
4069 }
4071 if (new_index == -1) {
4072 // no preset with this name, create
4073 new_index = total_prefs + 1;
4074 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4075 pref_path = create_pref("tools.calligraphic.preset", profile_id);
4076 g_free(profile_id);
4077 }
4079 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4080 gchar const *const widget_name = widget_names[i];
4081 void *widget = g_object_get_data(tbl, widget_name);
4082 if (widget) {
4083 if (GTK_IS_ADJUSTMENT(widget)) {
4084 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4085 double v = gtk_adjustment_get_value(adj);
4086 prefs_set_double_attribute(pref_path, widget_name, v);
4087 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4088 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4089 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4090 int v = gtk_toggle_action_get_active(toggle);
4091 prefs_set_int_attribute(pref_path, widget_name, v);
4092 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4093 } else {
4094 g_warning("Unknown widget type for preset: %s\n", widget_name);
4095 }
4096 } else {
4097 g_warning("Bad key when writing preset: %s\n", widget_name);
4098 }
4099 }
4100 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4102 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4104 sp_dcc_build_presets_list (tbl);
4106 g_free(pref_path);
4107 }
4110 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4112 gint preset_index = ege_select_one_action_get_active( act );
4113 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4115 if (preset_index == save_presets_index) {
4116 // this is the Save command
4117 sp_dcc_save_profile(NULL, tbl);
4118 return;
4119 }
4121 if (g_object_get_data(tbl, "presets_blocked"))
4122 return;
4124 gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4126 if (preset_path) {
4127 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
4129 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4131 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4132 iter;
4133 ++iter ) {
4134 const gchar *attr_name = g_quark_to_string(iter->key);
4135 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4136 continue;
4137 void *widget = g_object_get_data(tbl, attr_name);
4138 if (widget) {
4139 if (GTK_IS_ADJUSTMENT(widget)) {
4140 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4141 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4142 gtk_adjustment_set_value(adj, v);
4143 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4144 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4145 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4146 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4147 gtk_toggle_action_set_active(toggle, v);
4148 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4149 } else {
4150 g_warning("Unknown widget type for preset: %s\n", attr_name);
4151 }
4152 } else {
4153 g_warning("Bad key found in a preset record: %s\n", attr_name);
4154 }
4155 }
4156 g_free(preset_path);
4157 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4158 }
4160 }
4163 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4164 {
4165 {
4166 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4168 EgeAdjustmentAction* calligraphy_angle = 0;
4170 {
4171 /* Width */
4172 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4173 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4174 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4175 _("Pen Width"), _("Width:"),
4176 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4177 "tools.calligraphic", "width", 15,
4178 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4179 1, 100, 1.0, 0.0,
4180 labels, values, G_N_ELEMENTS(labels),
4181 sp_ddc_width_value_changed, 0.01, 0, 100 );
4182 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4183 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4184 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4185 }
4187 {
4188 /* Thinning */
4189 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4190 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4191 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4192 _("Stroke Thinning"), _("Thinning:"),
4193 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4194 "tools.calligraphic", "thinning", 10,
4195 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4196 -100, 100, 1, 0.1,
4197 labels, values, G_N_ELEMENTS(labels),
4198 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4199 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4200 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4201 }
4203 {
4204 /* Angle */
4205 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4206 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4207 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4208 _("Pen Angle"), _("Angle:"),
4209 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4210 "tools.calligraphic", "angle", 30,
4211 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4212 -90.0, 90.0, 1.0, 10.0,
4213 labels, values, G_N_ELEMENTS(labels),
4214 sp_ddc_angle_value_changed, 1, 0 );
4215 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4216 g_object_set_data( holder, "angle_action", eact );
4217 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4218 calligraphy_angle = eact;
4219 }
4221 {
4222 /* Fixation */
4223 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4224 gdouble values[] = {0, 20, 40, 60, 90, 100};
4225 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4226 _("Fixation"), _("Fixation:"),
4227 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4228 "tools.calligraphic", "flatness", 90,
4229 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4230 0.0, 100, 1.0, 10.0,
4231 labels, values, G_N_ELEMENTS(labels),
4232 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4233 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4234 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4235 }
4237 {
4238 /* Cap Rounding */
4239 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4240 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4241 // TRANSLATORS: "cap" means "end" (both start and finish) here
4242 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4243 _("Cap rounding"), _("Caps:"),
4244 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4245 "tools.calligraphic", "cap_rounding", 0.0,
4246 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4247 0.0, 5.0, 0.01, 0.1,
4248 labels, values, G_N_ELEMENTS(labels),
4249 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4250 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4251 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4252 }
4254 {
4255 /* Tremor */
4256 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4257 gdouble values[] = {0, 10, 20, 40, 60, 100};
4258 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4259 _("Stroke Tremor"), _("Tremor:"),
4260 _("Increase to make strokes rugged and trembling"),
4261 "tools.calligraphic", "tremor", 0.0,
4262 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4263 0.0, 100, 1, 0.0,
4264 labels, values, G_N_ELEMENTS(labels),
4265 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4267 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4268 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4269 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4270 }
4272 {
4273 /* Wiggle */
4274 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4275 gdouble values[] = {0, 20, 40, 60, 100};
4276 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4277 _("Pen Wiggle"), _("Wiggle:"),
4278 _("Increase to make the pen waver and wiggle"),
4279 "tools.calligraphic", "wiggle", 0.0,
4280 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4281 0.0, 100, 1, 0.0,
4282 labels, values, G_N_ELEMENTS(labels),
4283 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4284 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4285 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4286 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4287 }
4289 {
4290 /* Mass */
4291 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4292 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4293 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4294 _("Pen Mass"), _("Mass:"),
4295 _("Increase to make the pen drag behind, as if slowed by inertia"),
4296 "tools.calligraphic", "mass", 2.0,
4297 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4298 0.0, 100, 1, 0.0,
4299 labels, values, G_N_ELEMENTS(labels),
4300 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4301 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4302 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4303 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4304 }
4307 /* Trace Background button */
4308 {
4309 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4310 _("Trace Background"),
4311 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4312 "trace_background",
4313 Inkscape::ICON_SIZE_DECORATION );
4314 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4315 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4316 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4317 g_object_set_data( holder, "tracebackground", act );
4318 }
4320 /* Use Pressure button */
4321 {
4322 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4323 _("Pressure"),
4324 _("Use the pressure of the input device to alter the width of the pen"),
4325 "use_pressure",
4326 Inkscape::ICON_SIZE_DECORATION );
4327 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4328 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4329 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4330 g_object_set_data( holder, "usepressure", act );
4331 }
4333 /* Use Tilt button */
4334 {
4335 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4336 _("Tilt"),
4337 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4338 "use_tilt",
4339 Inkscape::ICON_SIZE_DECORATION );
4340 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4341 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4342 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4343 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4344 g_object_set_data( holder, "usetilt", act );
4345 }
4347 /*calligraphic profile */
4348 {
4349 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4350 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4351 ege_select_one_action_set_appearance (act1, "compact");
4352 g_object_set_data (holder, "profile_selector", act1 );
4354 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4356 sp_dcc_build_presets_list (holder);
4358 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4359 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4360 }
4361 }
4362 }
4365 //########################
4366 //## Circle / Arc ##
4367 //########################
4369 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4370 {
4371 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4372 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4374 if (v1 == 0 && v2 == 0) {
4375 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4376 gtk_action_set_sensitive( ocb, FALSE );
4377 gtk_action_set_sensitive( make_whole, FALSE );
4378 }
4379 } else {
4380 gtk_action_set_sensitive( ocb, TRUE );
4381 gtk_action_set_sensitive( make_whole, TRUE );
4382 }
4383 }
4385 static void
4386 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4387 {
4388 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4390 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4391 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4392 }
4394 // quit if run by the attr_changed listener
4395 if (g_object_get_data( tbl, "freeze" )) {
4396 return;
4397 }
4399 // in turn, prevent listener from responding
4400 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4402 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4404 bool modmade = false;
4405 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4406 items != NULL;
4407 items = items->next)
4408 {
4409 SPItem *item = SP_ITEM(items->data);
4411 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4413 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4414 SPArc *arc = SP_ARC(item);
4416 if (!strcmp(value_name, "start"))
4417 ge->start = (adj->value * M_PI)/ 180;
4418 else
4419 ge->end = (adj->value * M_PI)/ 180;
4421 sp_genericellipse_normalize(ge);
4422 ((SPObject *)arc)->updateRepr();
4423 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4425 modmade = true;
4426 }
4427 }
4429 g_free(namespaced_name);
4431 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4433 sp_arctb_sensitivize( tbl, adj->value, other->value );
4435 if (modmade) {
4436 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4437 _("Arc: Change start/end"));
4438 }
4440 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4441 }
4444 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4445 {
4446 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4447 }
4449 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4450 {
4451 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4452 }
4455 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4456 {
4457 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4458 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4459 if ( ege_select_one_action_get_active( act ) != 0 ) {
4460 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4461 } else {
4462 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4463 }
4464 }
4466 // quit if run by the attr_changed listener
4467 if (g_object_get_data( tbl, "freeze" )) {
4468 return;
4469 }
4471 // in turn, prevent listener from responding
4472 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4474 bool modmade = false;
4476 if ( ege_select_one_action_get_active(act) != 0 ) {
4477 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4478 items != NULL;
4479 items = items->next)
4480 {
4481 if (SP_IS_ARC((SPItem *) items->data)) {
4482 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4483 repr->setAttribute("sodipodi:open", "true");
4484 SP_OBJECT((SPItem *) items->data)->updateRepr();
4485 modmade = true;
4486 }
4487 }
4488 } else {
4489 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4490 items != NULL;
4491 items = items->next)
4492 {
4493 if (SP_IS_ARC((SPItem *) items->data)) {
4494 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4495 repr->setAttribute("sodipodi:open", NULL);
4496 SP_OBJECT((SPItem *) items->data)->updateRepr();
4497 modmade = true;
4498 }
4499 }
4500 }
4502 if (modmade) {
4503 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4504 _("Arc: Change open/closed"));
4505 }
4507 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4508 }
4510 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4511 {
4512 GtkAdjustment *adj;
4513 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4514 gtk_adjustment_set_value(adj, 0.0);
4515 gtk_adjustment_value_changed(adj);
4517 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4518 gtk_adjustment_set_value(adj, 0.0);
4519 gtk_adjustment_value_changed(adj);
4521 spinbutton_defocus( GTK_OBJECT(obj) );
4522 }
4524 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4525 gchar const */*old_value*/, gchar const */*new_value*/,
4526 bool /*is_interactive*/, gpointer data)
4527 {
4528 GObject *tbl = G_OBJECT(data);
4530 // quit if run by the _changed callbacks
4531 if (g_object_get_data( tbl, "freeze" )) {
4532 return;
4533 }
4535 // in turn, prevent callbacks from responding
4536 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4538 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4539 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4541 GtkAdjustment *adj1,*adj2;
4542 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4543 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4544 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4545 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4547 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4549 char const *openstr = NULL;
4550 openstr = repr->attribute("sodipodi:open");
4551 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4553 if (openstr) {
4554 ege_select_one_action_set_active( ocb, 1 );
4555 } else {
4556 ege_select_one_action_set_active( ocb, 0 );
4557 }
4559 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4560 }
4562 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4563 NULL, /* child_added */
4564 NULL, /* child_removed */
4565 arc_tb_event_attr_changed,
4566 NULL, /* content_changed */
4567 NULL /* order_changed */
4568 };
4571 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4572 {
4573 int n_selected = 0;
4574 Inkscape::XML::Node *repr = NULL;
4576 purge_repr_listener( tbl, tbl );
4578 for (GSList const *items = selection->itemList();
4579 items != NULL;
4580 items = items->next)
4581 {
4582 if (SP_IS_ARC((SPItem *) items->data)) {
4583 n_selected++;
4584 repr = SP_OBJECT_REPR((SPItem *) items->data);
4585 }
4586 }
4588 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4590 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4591 if (n_selected == 0) {
4592 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4593 } else if (n_selected == 1) {
4594 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4595 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4597 if (repr) {
4598 g_object_set_data( tbl, "repr", repr );
4599 Inkscape::GC::anchor(repr);
4600 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4601 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4602 }
4603 } else {
4604 // FIXME: implement averaging of all parameters for multiple selected
4605 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4606 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4607 sp_arctb_sensitivize( tbl, 1, 0 );
4608 }
4609 }
4612 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4613 {
4614 EgeAdjustmentAction* eact = 0;
4615 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4618 {
4619 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4620 ege_output_action_set_use_markup( act, TRUE );
4621 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4622 g_object_set_data( holder, "mode_action", act );
4623 }
4625 /* Start */
4626 {
4627 eact = create_adjustment_action( "ArcStartAction",
4628 _("Start"), _("Start:"),
4629 _("The angle (in degrees) from the horizontal to the arc's start point"),
4630 "tools.shapes.arc", "start", 0.0,
4631 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4632 -360.0, 360.0, 1.0, 10.0,
4633 0, 0, 0,
4634 sp_arctb_start_value_changed);
4635 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4636 }
4638 /* End */
4639 {
4640 eact = create_adjustment_action( "ArcEndAction",
4641 _("End"), _("End:"),
4642 _("The angle (in degrees) from the horizontal to the arc's end point"),
4643 "tools.shapes.arc", "end", 0.0,
4644 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4645 -360.0, 360.0, 1.0, 10.0,
4646 0, 0, 0,
4647 sp_arctb_end_value_changed);
4648 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4649 }
4651 /* Segments / Pie checkbox */
4652 {
4653 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4655 GtkTreeIter iter;
4656 gtk_list_store_append( model, &iter );
4657 gtk_list_store_set( model, &iter,
4658 0, _("Closed arc"),
4659 1, _("Switch to segment (closed shape with two radii)"),
4660 2, "circle_closed_arc",
4661 -1 );
4663 gtk_list_store_append( model, &iter );
4664 gtk_list_store_set( model, &iter,
4665 0, _("Open Arc"),
4666 1, _("Switch to arc (unclosed shape)"),
4667 2, "circle_open_arc",
4668 -1 );
4670 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4671 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4672 g_object_set_data( holder, "open_action", act );
4674 ege_select_one_action_set_appearance( act, "full" );
4675 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4676 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4677 ege_select_one_action_set_icon_column( act, 2 );
4678 ege_select_one_action_set_icon_size( act, secondarySize );
4679 ege_select_one_action_set_tooltip_column( act, 1 );
4681 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4682 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4683 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4684 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4685 }
4687 /* Make Whole */
4688 {
4689 InkAction* inky = ink_action_new( "ArcResetAction",
4690 _("Make whole"),
4691 _("Make the shape a whole ellipse, not arc or segment"),
4692 "reset_circle",
4693 secondarySize );
4694 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4695 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4696 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4697 g_object_set_data( holder, "make_whole", inky );
4698 }
4700 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4701 // sensitivize make whole and open checkbox
4702 {
4703 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4704 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4705 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4706 }
4709 sigc::connection *connection = new sigc::connection(
4710 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4711 );
4712 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4713 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4714 }
4719 // toggle button callbacks and updaters
4721 //########################
4722 //## Dropper ##
4723 //########################
4725 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4726 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4727 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4728 if ( set_action ) {
4729 if ( gtk_toggle_action_get_active( act ) ) {
4730 gtk_action_set_sensitive( set_action, TRUE );
4731 } else {
4732 gtk_action_set_sensitive( set_action, FALSE );
4733 }
4734 }
4736 spinbutton_defocus(GTK_OBJECT(tbl));
4737 }
4739 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4740 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4741 spinbutton_defocus(GTK_OBJECT(tbl));
4742 }
4745 /**
4746 * Dropper auxiliary toolbar construction and setup.
4747 *
4748 * TODO: Would like to add swatch of current color.
4749 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4750 * can drag and drop places. Will provide a nice mixing palette.
4751 */
4752 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4753 {
4754 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4756 {
4757 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4758 ege_output_action_set_use_markup( act, TRUE );
4759 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4760 }
4762 {
4763 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4764 _("Pick opacity"),
4765 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4766 NULL,
4767 Inkscape::ICON_SIZE_DECORATION );
4768 g_object_set( act, "short_label", _("Pick"), NULL );
4769 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4770 g_object_set_data( holder, "pick_action", act );
4771 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4772 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4773 }
4775 {
4776 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4777 _("Assign opacity"),
4778 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4779 NULL,
4780 Inkscape::ICON_SIZE_DECORATION );
4781 g_object_set( act, "short_label", _("Assign"), NULL );
4782 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4783 g_object_set_data( holder, "set_action", act );
4784 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4785 // make sure it's disabled if we're not picking alpha
4786 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4787 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4788 }
4789 }
4792 //########################
4793 //## LPETool ##
4794 //########################
4796 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4798 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4799 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4800 {
4801 using namespace Inkscape::LivePathEffect;
4803 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4804 SPEventContext *ec = desktop->event_context;
4805 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4806 return;
4807 }
4809 // only take action if run by the attr_changed listener
4810 if (!g_object_get_data(tbl, "freeze")) {
4811 // in turn, prevent listener from responding
4812 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4814 gint mode = ege_select_one_action_get_active(act);
4815 EffectType type = lpesubtools[mode];
4817 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4818 bool success = lpetool_try_construction(lc, type);
4819 if (success) {
4820 // since the construction was already performed, we set the state back to inactive
4821 ege_select_one_action_set_active(act, 0);
4822 mode = 0;
4823 } else {
4824 // switch to the chosen subtool
4825 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4826 }
4828 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4829 prefs_set_int_attribute( "tools.lpetool", "mode", mode );
4830 }
4832 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4833 }
4834 }
4836 void
4837 sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4838 {
4839 SPEventContext *ec = selection->desktop()->event_context;
4840 if (!SP_IS_LPETOOL_CONTEXT(ec))
4841 return;
4843 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4844 }
4846 void
4847 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4848 {
4849 using namespace Inkscape::LivePathEffect;
4850 SPEventContext *ec = selection->desktop()->event_context;
4851 if (!SP_IS_LPETOOL_CONTEXT(ec))
4852 return;
4853 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4855 lpetool_delete_measuring_items(lc);
4856 lpetool_create_measuring_items(lc, selection);
4858 // activate line segment combo box if a single item with LPELineSegment is selected
4859 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4860 SPItem *item = selection->singleItem();
4861 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4862 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4863 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4864 if (lpe && lpe->effectType() == LINE_SEGMENT) {
4865 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4866 g_object_set_data(tbl, "currentlpe", lpe);
4867 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4868 gtk_action_set_sensitive(w, TRUE);
4869 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
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 } else {
4876 g_object_set_data(tbl, "currentlpe", NULL);
4877 g_object_set_data(tbl, "currentlpeitem", NULL);
4878 gtk_action_set_sensitive(w, FALSE);
4879 }
4880 }
4882 static void
4883 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4884 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4886 bool show = gtk_toggle_action_get_active( act );
4887 prefs_set_int_attribute ("tools.lpetool", "show_bbox", show ? 1 : 0);
4889 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4890 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4891 lpetool_context_reset_limiting_bbox(lc);
4892 }
4893 }
4895 static void
4896 lpetool_toggle_show_measuring_info (GtkToggleAction *act, gpointer data) {
4897 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4898 if (!tools_isactive(desktop, TOOLS_LPETOOL))
4899 return;
4901 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4902 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4903 bool show = gtk_toggle_action_get_active( act );
4904 prefs_set_int_attribute ("tools.lpetool", "show_measuring_info", show ? 1 : 0);
4905 lpetool_show_measuring_info(lc, show);
4906 }
4907 }
4909 static void
4910 lpetool_unit_changed(GtkAction* act, GObject* tbl) {
4911 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4912 SPUnit const *unit = tracker->getActiveUnit();
4913 prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
4915 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4916 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4917 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4918 lpetool_delete_measuring_items(lc);
4919 lpetool_create_measuring_items(lc);
4920 }
4921 }
4923 static void
4924 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4925 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4926 Inkscape::Selection *selection = desktop->selection;
4928 boost::optional<NR::Rect> bbox = selection->bounds();
4930 if (bbox) {
4931 Geom::Point A(bbox->min());
4932 Geom::Point B(bbox->max());
4934 A *= desktop->doc2dt();
4935 B *= desktop->doc2dt();
4937 // TODO: should we provide a way to store points in prefs?
4938 prefs_set_double_attribute ("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4939 prefs_set_double_attribute ("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4940 prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4941 prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4943 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4944 }
4946 gtk_toggle_action_set_active(act, false);
4947 }
4949 static void
4950 sp_line_segment_build_list(GObject *tbl)
4951 {
4952 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
4954 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
4955 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4956 gtk_list_store_clear (model);
4958 // TODO: we add the entries of rht combo box manually; later this should be done automatically
4959 {
4960 GtkTreeIter iter;
4961 gtk_list_store_append( model, &iter );
4962 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
4963 gtk_list_store_append( model, &iter );
4964 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
4965 gtk_list_store_append( model, &iter );
4966 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
4967 gtk_list_store_append( model, &iter );
4968 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
4969 }
4971 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
4972 }
4974 static void
4975 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
4976 using namespace Inkscape::LivePathEffect;
4978 // quit if run by the attr_changed listener
4979 if (g_object_get_data(tbl, "freeze")) {
4980 return;
4981 }
4983 // in turn, prevent listener from responding
4984 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4986 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
4987 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4988 if (lpeitem) {
4989 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4990 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
4991 sp_lpe_item_update_patheffect(lpeitem, true, true);
4992 }
4994 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4995 }
4997 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4998 {
4999 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5000 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5001 g_object_set_data(holder, "tracker", tracker);
5002 SPUnit const *unit = tracker->getActiveUnit();
5003 prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
5005 /** Automatically create a list of LPEs that get added to the toolbar **/
5006 {
5007 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5009 GtkTreeIter iter;
5011 // the first toggle button represents the state that no subtool is active (remove this when
5012 // this can be modeled by EgeSelectOneAction or some other action)
5013 gtk_list_store_append( model, &iter );
5014 gtk_list_store_set( model, &iter,
5015 0, _("All inactive"),
5016 1, _("No geometric tool is active"),
5017 2, _("all_inactive"),
5018 -1 );
5020 Inkscape::LivePathEffect::EffectType type;
5021 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5022 type = lpesubtools[i];
5023 gtk_list_store_append( model, &iter );
5024 gtk_list_store_set( model, &iter,
5025 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5026 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5027 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5028 -1 );
5029 }
5031 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5032 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5033 g_object_set_data( holder, "lpetool_mode_action", act );
5035 ege_select_one_action_set_appearance( act, "full" );
5036 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5037 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5038 ege_select_one_action_set_icon_column( act, 2 );
5039 ege_select_one_action_set_tooltip_column( act, 1 );
5041 gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
5042 ege_select_one_action_set_active( act, lpeToolMode );
5043 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5044 }
5046 /* Show limiting bounding box */
5047 {
5048 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5049 _("Show limiting bounding box"),
5050 _("Show bounding box (used to cut infinite lines)"),
5051 "lpetool_show_bbox",
5052 Inkscape::ICON_SIZE_DECORATION );
5053 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5054 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5055 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
5056 }
5058 /* Set limiting bounding box to bbox of current selection */
5059 {
5060 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5061 _("Get limiting bounding box from selection"),
5062 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5063 "lpetool_set_bbox",
5064 Inkscape::ICON_SIZE_DECORATION );
5065 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5066 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5067 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5068 }
5071 /* Combo box to choose line segment type */
5072 {
5073 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5074 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5075 ege_select_one_action_set_appearance (act, "compact");
5076 g_object_set_data (holder, "lpetool_line_segment_action", act );
5078 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5080 sp_line_segment_build_list (holder);
5082 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5083 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5084 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5085 }
5087 /* Display measuring info for selected items */
5088 {
5089 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5090 _("Display measuring info"),
5091 _("Display measuring info for selected items"),
5092 "lpetool_measuring_info",
5093 Inkscape::ICON_SIZE_DECORATION );
5094 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5095 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), desktop );
5096 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_measuring_info", 1 ) );
5097 }
5099 // add the units menu
5100 {
5101 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5102 gtk_action_group_add_action( mainActions, act );
5103 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5104 }
5106 //watch selection
5107 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5109 sigc::connection *c_selection_modified =
5110 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5111 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5112 pool->add_connection ("selection-modified", c_selection_modified);
5114 sigc::connection *c_selection_changed =
5115 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5116 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5117 pool->add_connection ("selection-changed", c_selection_changed);
5118 }
5120 //########################
5121 //## Eraser ##
5122 //########################
5124 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5125 {
5126 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5127 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5128 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5129 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
5130 }
5132 // only take action if run by the attr_changed listener
5133 if (!g_object_get_data( tbl, "freeze" )) {
5134 // in turn, prevent listener from responding
5135 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5137 if ( eraserMode != 0 ) {
5138 } else {
5139 }
5140 // TODO finish implementation
5142 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5143 }
5144 }
5146 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5147 {
5148 {
5149 /* Width */
5150 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5151 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5152 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5153 _("Pen Width"), _("Width:"),
5154 _("The width of the eraser pen (relative to the visible canvas area)"),
5155 "tools.eraser", "width", 15,
5156 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5157 1, 100, 1.0, 0.0,
5158 labels, values, G_N_ELEMENTS(labels),
5159 sp_ddc_width_value_changed, 0.01, 0, 100 );
5160 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5161 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5162 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5163 }
5165 {
5166 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5168 GtkTreeIter iter;
5169 gtk_list_store_append( model, &iter );
5170 gtk_list_store_set( model, &iter,
5171 0, _("Delete"),
5172 1, _("Delete objects touched by the eraser"),
5173 2, "delete_object",
5174 -1 );
5176 gtk_list_store_append( model, &iter );
5177 gtk_list_store_set( model, &iter,
5178 0, _("Cut"),
5179 1, _("Cut out from objects"),
5180 2, "difference",
5181 -1 );
5183 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5184 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5185 g_object_set_data( holder, "eraser_mode_action", act );
5187 ege_select_one_action_set_appearance( act, "full" );
5188 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5189 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5190 ege_select_one_action_set_icon_column( act, 2 );
5191 ege_select_one_action_set_tooltip_column( act, 1 );
5193 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5194 ege_select_one_action_set_active( act, eraserMode );
5195 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5196 }
5198 }
5200 //########################
5201 //## Text Toolbox ##
5202 //########################
5203 /*
5204 static void
5205 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5206 {
5207 //Call back for letter sizing spinbutton
5208 }
5210 static void
5211 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5212 {
5213 //Call back for line height spinbutton
5214 }
5216 static void
5217 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5218 {
5219 //Call back for horizontal kerning spinbutton
5220 }
5222 static void
5223 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5224 {
5225 //Call back for vertical kerning spinbutton
5226 }
5228 static void
5229 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5230 {
5231 //Call back for letter rotation spinbutton
5232 }*/
5234 namespace {
5236 bool popdown_visible = false;
5237 bool popdown_hasfocus = false;
5239 void
5240 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5241 {
5242 SPStyle *query =
5243 sp_style_new (SP_ACTIVE_DOCUMENT);
5245 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5247 int result_family =
5248 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5250 int result_style =
5251 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5253 int result_numbers =
5254 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5256 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5258 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5259 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5260 // there are no texts in selection, read from prefs
5262 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5263 if (repr) {
5264 sp_style_read_from_repr (query, repr);
5265 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5266 // do not reset the toolbar style from prefs if we already did it last time
5267 sp_style_unref(query);
5268 return;
5269 }
5270 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5271 } else {
5272 sp_style_unref(query);
5273 return;
5274 }
5275 } else {
5276 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5277 }
5279 if (query->text)
5280 {
5281 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5282 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5283 gtk_entry_set_text (GTK_ENTRY (entry), "");
5285 } else if (query->text->font_specification.value || query->text->font_family.value) {
5287 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5289 // Get the font that corresponds
5290 Glib::ustring familyName;
5292 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5293 if (font) {
5294 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5295 font->Unref();
5296 font = NULL;
5297 }
5299 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5301 Gtk::TreePath path;
5302 try {
5303 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5304 } catch (...) {
5305 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5306 sp_style_unref(query);
5307 return;
5308 }
5310 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5311 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5313 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5315 gtk_tree_selection_select_path (tselection, path.gobj());
5316 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5318 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5319 }
5321 //Size
5322 {
5323 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5324 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5325 g_object_set_data(tbl, "size-block", gpointer(1));
5326 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5327 g_object_set_data(tbl, "size-block", gpointer(0));
5328 g_free(str);
5329 }
5331 //Anchor
5332 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5333 {
5334 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5335 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5336 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5337 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5338 }
5339 else
5340 {
5341 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5342 {
5343 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5344 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5345 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5346 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5347 }
5348 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5349 {
5350 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5351 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5352 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5353 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5354 }
5355 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5356 {
5357 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5358 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5359 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5360 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5361 }
5362 }
5364 //Style
5365 {
5366 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5368 gboolean active = gtk_toggle_button_get_active (button);
5369 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5371 if (active != check)
5372 {
5373 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5374 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5375 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5376 }
5377 }
5379 {
5380 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5382 gboolean active = gtk_toggle_button_get_active (button);
5383 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5385 if (active != check)
5386 {
5387 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5388 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5389 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5390 }
5391 }
5393 //Orientation
5394 //locking both buttons, changing one affect all group (both)
5395 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5396 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5398 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5399 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5401 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5402 {
5403 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5404 }
5405 else
5406 {
5407 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5408 }
5409 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5410 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5411 }
5413 sp_style_unref(query);
5414 }
5416 void
5417 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5418 {
5419 sp_text_toolbox_selection_changed (selection, tbl);
5420 }
5422 void
5423 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5424 {
5425 sp_text_toolbox_selection_changed (NULL, tbl);
5426 }
5428 void
5429 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5430 GObject *tbl)
5431 {
5432 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5433 GtkTreeModel *model = 0;
5434 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5435 GtkTreeIter iter;
5436 char *family = 0;
5438 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5439 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5441 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5442 return;
5443 }
5445 gtk_tree_model_get (model, &iter, 0, &family, -1);
5447 if (g_object_get_data (G_OBJECT (selection), "block"))
5448 {
5449 gtk_entry_set_text (GTK_ENTRY (entry), family);
5450 return;
5451 }
5453 gtk_entry_set_text (GTK_ENTRY (entry), family);
5455 SPStyle *query =
5456 sp_style_new (SP_ACTIVE_DOCUMENT);
5458 int result_fontspec =
5459 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5461 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5463 SPCSSAttr *css = sp_repr_css_attr_new ();
5466 // First try to get the font spec from the stored value
5467 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5469 if (fontSpec.empty()) {
5470 // Construct a new font specification if it does not yet exist
5471 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5472 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5473 fontFromStyle->Unref();
5474 }
5476 if (!fontSpec.empty()) {
5477 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5478 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5479 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5480 if (font) {
5481 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5483 // Set all the these just in case they were altered when finding the best
5484 // match for the new family and old style...
5486 gchar c[256];
5488 font->Family(c, 256);
5489 sp_repr_css_set_property (css, "font-family", c);
5491 font->Attribute( "weight", c, 256);
5492 sp_repr_css_set_property (css, "font-weight", c);
5494 font->Attribute("style", c, 256);
5495 sp_repr_css_set_property (css, "font-style", c);
5497 font->Attribute("stretch", c, 256);
5498 sp_repr_css_set_property (css, "font-stretch", c);
5500 font->Attribute("variant", c, 256);
5501 sp_repr_css_set_property (css, "font-variant", c);
5503 font->Unref();
5504 }
5505 }
5506 }
5508 // If querying returned nothing, set the default style of the tool (for new texts)
5509 if (result_fontspec == QUERY_STYLE_NOTHING)
5510 {
5511 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5512 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5513 }
5514 else
5515 {
5516 sp_desktop_set_style (desktop, css, true, true);
5517 }
5519 sp_style_unref(query);
5521 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5522 _("Text: Change font family"));
5523 sp_repr_css_attr_unref (css);
5524 g_free(family);
5525 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5527 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5528 }
5530 /* This is where execution comes when the contents of the font family box have been completed
5531 by the press of the return key */
5532 void
5533 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5534 GObject *tbl)
5535 {
5536 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5538 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5539 try {
5540 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5541 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5542 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5543 gtk_tree_selection_select_path (selection, path.gobj());
5544 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5545 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5546 } catch (...) {
5547 if (family && strlen (family))
5548 {
5549 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5550 }
5551 }
5552 }
5554 void
5555 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5556 gpointer data)
5557 {
5558 if (g_object_get_data (G_OBJECT (button), "block")) return;
5559 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5560 int prop = GPOINTER_TO_INT(data);
5562 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5563 SPCSSAttr *css = sp_repr_css_attr_new ();
5565 switch (prop)
5566 {
5567 case 0:
5568 {
5569 sp_repr_css_set_property (css, "text-anchor", "start");
5570 sp_repr_css_set_property (css, "text-align", "start");
5571 break;
5572 }
5573 case 1:
5574 {
5575 sp_repr_css_set_property (css, "text-anchor", "middle");
5576 sp_repr_css_set_property (css, "text-align", "center");
5577 break;
5578 }
5580 case 2:
5581 {
5582 sp_repr_css_set_property (css, "text-anchor", "end");
5583 sp_repr_css_set_property (css, "text-align", "end");
5584 break;
5585 }
5587 case 3:
5588 {
5589 sp_repr_css_set_property (css, "text-anchor", "start");
5590 sp_repr_css_set_property (css, "text-align", "justify");
5591 break;
5592 }
5593 }
5595 SPStyle *query =
5596 sp_style_new (SP_ACTIVE_DOCUMENT);
5597 int result_numbers =
5598 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5600 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5601 if (result_numbers == QUERY_STYLE_NOTHING)
5602 {
5603 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5604 }
5606 sp_style_unref(query);
5608 sp_desktop_set_style (desktop, css, true, true);
5609 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5610 _("Text: Change alignment"));
5611 sp_repr_css_attr_unref (css);
5613 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5614 }
5616 void
5617 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5618 gpointer data)
5619 {
5620 if (g_object_get_data (G_OBJECT (button), "block")) return;
5622 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5623 SPCSSAttr *css = sp_repr_css_attr_new ();
5624 int prop = GPOINTER_TO_INT(data);
5625 bool active = gtk_toggle_button_get_active (button);
5627 SPStyle *query =
5628 sp_style_new (SP_ACTIVE_DOCUMENT);
5630 int result_fontspec =
5631 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5633 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5634 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5635 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5637 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5638 Glib::ustring newFontSpec = "";
5640 if (fontSpec.empty()) {
5641 // Construct a new font specification if it does not yet exist
5642 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5643 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5644 fontFromStyle->Unref();
5645 }
5647 switch (prop)
5648 {
5649 case 0:
5650 {
5651 if (!fontSpec.empty()) {
5652 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5653 }
5654 if (fontSpec != newFontSpec) {
5655 // Don't even set the bold if the font didn't exist on the system
5656 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5657 }
5658 break;
5659 }
5661 case 1:
5662 {
5663 if (!fontSpec.empty()) {
5664 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5665 }
5666 if (fontSpec != newFontSpec) {
5667 // Don't even set the italic if the font didn't exist on the system
5668 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5669 }
5670 break;
5671 }
5672 }
5674 if (!newFontSpec.empty()) {
5675 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5676 }
5678 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5679 if (result_fontspec == QUERY_STYLE_NOTHING)
5680 {
5681 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5682 }
5684 sp_style_unref(query);
5686 sp_desktop_set_style (desktop, css, true, true);
5687 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5688 _("Text: Change font style"));
5689 sp_repr_css_attr_unref (css);
5691 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5692 }
5694 void
5695 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5696 gpointer data)
5697 {
5698 if (g_object_get_data (G_OBJECT (button), "block")) {
5699 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5700 return;
5701 }
5703 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5704 SPCSSAttr *css = sp_repr_css_attr_new ();
5705 int prop = GPOINTER_TO_INT(data);
5707 switch (prop)
5708 {
5709 case 0:
5710 {
5711 sp_repr_css_set_property (css, "writing-mode", "lr");
5712 break;
5713 }
5715 case 1:
5716 {
5717 sp_repr_css_set_property (css, "writing-mode", "tb");
5718 break;
5719 }
5720 }
5722 SPStyle *query =
5723 sp_style_new (SP_ACTIVE_DOCUMENT);
5724 int result_numbers =
5725 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5727 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5728 if (result_numbers == QUERY_STYLE_NOTHING)
5729 {
5730 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5731 }
5733 sp_desktop_set_style (desktop, css, true, true);
5734 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5735 _("Text: Change orientation"));
5736 sp_repr_css_attr_unref (css);
5738 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5739 }
5741 gboolean
5742 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5743 {
5744 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5745 if (!desktop) return FALSE;
5747 switch (get_group0_keyval (event)) {
5748 case GDK_Escape: // defocus
5749 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5750 sp_text_toolbox_selection_changed (NULL, tbl); // update
5751 return TRUE; // I consumed the event
5752 break;
5753 }
5754 return FALSE;
5755 }
5757 gboolean
5758 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5759 {
5760 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5761 if (!desktop) return FALSE;
5763 switch (get_group0_keyval (event)) {
5764 case GDK_KP_Enter:
5765 case GDK_Return:
5766 case GDK_Escape: // defocus
5767 gtk_widget_hide (w);
5768 popdown_visible = false;
5769 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5770 return TRUE; // I consumed the event
5771 break;
5772 case GDK_w:
5773 case GDK_W:
5774 if (event->state & GDK_CONTROL_MASK) {
5775 gtk_widget_hide (w);
5776 popdown_visible = false;
5777 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5778 return TRUE; // I consumed the event
5779 }
5780 break;
5781 }
5782 return FALSE;
5783 }
5786 void
5787 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5788 GObject *tbl)
5789 {
5790 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5792 if (g_object_get_data (tbl, "size-block")) return;
5794 // If this is not from selecting a size in the list (in which case get_active will give the
5795 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5796 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5797 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5798 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5799 return;
5801 gdouble value = -1;
5802 {
5803 gchar *endptr;
5804 gchar *const text = gtk_combo_box_get_active_text(cbox);
5805 if (text) {
5806 value = g_strtod(text, &endptr);
5807 if (endptr == text) { // Conversion failed, non-numeric input.
5808 value = -1;
5809 }
5810 g_free(text);
5811 }
5812 }
5813 if (value <= 0) {
5814 return; // could not parse value
5815 }
5817 SPCSSAttr *css = sp_repr_css_attr_new ();
5818 Inkscape::CSSOStringStream osfs;
5819 osfs << value;
5820 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5822 SPStyle *query =
5823 sp_style_new (SP_ACTIVE_DOCUMENT);
5824 int result_numbers =
5825 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5827 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5828 if (result_numbers == QUERY_STYLE_NOTHING)
5829 {
5830 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5831 }
5833 sp_style_unref(query);
5835 sp_desktop_set_style (desktop, css, true, true);
5836 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5837 _("Text: Change font size"));
5838 sp_repr_css_attr_unref (css);
5840 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5841 }
5843 gboolean
5844 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5845 {
5846 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5847 if (!desktop) return FALSE;
5849 if (!g_object_get_data (tbl, "esc-pressed")) {
5850 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5851 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5852 sp_text_toolbox_size_changed (cbox, tbl);
5853 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5854 }
5855 return FALSE; // I consumed the event
5856 }
5859 gboolean
5860 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5861 {
5862 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5863 if (!desktop) return FALSE;
5865 switch (get_group0_keyval (event)) {
5866 case GDK_Escape: // defocus
5867 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5868 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5869 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5870 return TRUE; // I consumed the event
5871 break;
5872 case GDK_Return: // defocus
5873 case GDK_KP_Enter:
5874 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5875 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5876 sp_text_toolbox_size_changed (cbox, tbl);
5877 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5878 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5879 return TRUE; // I consumed the event
5880 break;
5881 }
5882 return FALSE;
5883 }
5885 void
5886 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5887 GObject *tbl)
5888 {
5889 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5890 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5891 int x, y;
5893 if (!popdown_visible)
5894 {
5895 gdk_window_get_origin (widget->window, &x, &y);
5896 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5897 gtk_widget_show_all (popdown);
5898 //sp_transientize (popdown);
5900 gdk_pointer_grab (widget->window, TRUE,
5901 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5902 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5903 GDK_POINTER_MOTION_MASK),
5904 NULL, NULL, GDK_CURRENT_TIME);
5906 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5908 popdown_visible = true;
5909 }
5910 else
5911 {
5912 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5913 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5914 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5915 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5916 gtk_widget_hide (popdown);
5917 popdown_visible = false;
5918 }
5919 }
5921 gboolean
5922 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5923 GdkEventFocus */*event*/,
5924 GObject */*tbl*/)
5925 {
5926 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5927 return FALSE;
5928 }
5930 gboolean
5931 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5932 GdkEventFocus */*event*/,
5933 GObject */*tbl*/)
5934 {
5935 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5937 if (popdown_hasfocus) {
5938 gtk_widget_hide (popdown);
5939 popdown_hasfocus = false;
5940 popdown_visible = false;
5941 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5942 return TRUE;
5943 }
5944 return FALSE;
5945 }
5947 gboolean
5948 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5949 GdkEventFocus */*event*/,
5950 GObject */*tbl*/)
5951 {
5952 popdown_hasfocus = true;
5953 return TRUE;
5954 }
5957 void
5958 cell_data_func (GtkTreeViewColumn */*column*/,
5959 GtkCellRenderer *cell,
5960 GtkTreeModel *tree_model,
5961 GtkTreeIter *iter,
5962 gpointer /*data*/)
5963 {
5964 gchar *family;
5965 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5966 gchar *const family_escaped = g_markup_escape_text(family, -1);
5968 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5969 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5971 std::stringstream markup;
5972 markup << family_escaped << " <span foreground='darkgray' font_family='"
5973 << family_escaped << "'>" << sample_escaped << "</span>";
5974 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5976 g_free(family);
5977 g_free(family_escaped);
5978 g_free(sample_escaped);
5979 }
5981 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5982 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5983 if (completion) {
5984 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5985 g_object_unref (completion);
5986 }
5987 }
5989 GtkWidget*
5990 sp_text_toolbox_new (SPDesktop *desktop)
5991 {
5992 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5993 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5995 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5996 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5998 GtkTooltips *tt = gtk_tooltips_new();
5999 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6001 ////////////Family
6002 //Window
6003 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6004 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6006 //Entry
6007 GtkWidget *entry = gtk_entry_new ();
6008 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6009 GtkEntryCompletion *completion = gtk_entry_completion_new ();
6010 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6011 gtk_entry_completion_set_text_column (completion, 0);
6012 gtk_entry_completion_set_minimum_key_length (completion, 1);
6013 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6014 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6015 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6016 gtk_toolbar_append_widget( tbl, entry, "", "" );
6017 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6019 //Button
6020 GtkWidget *button = gtk_button_new ();
6021 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6022 gtk_toolbar_append_widget( tbl, button, "", "");
6024 //Popdown
6025 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
6026 GtkWidget *treeview = gtk_tree_view_new ();
6028 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6029 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
6030 gtk_tree_view_column_pack_start (column, cell, FALSE);
6031 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6032 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6033 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6035 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6036 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6037 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6039 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6041 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6042 gtk_container_add (GTK_CONTAINER (sw), treeview);
6044 gtk_container_add (GTK_CONTAINER (window), sw);
6045 gtk_widget_set_size_request (window, 300, 450);
6047 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6048 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6049 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6051 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6053 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6054 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6055 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6057 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6058 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6060 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6061 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6062 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6063 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6064 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6066 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6067 GtkWidget *box = gtk_event_box_new ();
6068 gtk_container_add (GTK_CONTAINER (box), image);
6069 gtk_toolbar_append_widget( tbl, box, "", "");
6070 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6071 GtkTooltips *tooltips = gtk_tooltips_new ();
6072 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6073 gtk_widget_hide (GTK_WIDGET (box));
6074 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6076 ////////////Size
6077 gchar const *const sizes[] = {
6078 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6079 "16", "18", "20", "22", "24", "28",
6080 "32", "36", "40", "48", "56", "64", "72", "144"
6081 };
6083 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6084 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6085 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6086 }
6087 gtk_widget_set_size_request (cbox, 80, -1);
6088 gtk_toolbar_append_widget( tbl, cbox, "", "");
6089 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6090 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6091 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6092 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6094 ////////////Text anchor
6095 GtkWidget *group = gtk_radio_button_new (NULL);
6096 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6097 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6099 // left
6100 GtkWidget *rbutton = group;
6101 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6102 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6103 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6105 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6106 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6107 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6108 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6110 // center
6111 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6112 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6113 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6114 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6116 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6117 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6118 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6119 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6121 // right
6122 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6123 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6124 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6125 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6127 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6128 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6129 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6130 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6132 // fill
6133 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6134 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6135 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6136 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6138 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6139 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6140 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6141 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6143 gtk_toolbar_append_widget( tbl, row, "", "");
6145 //spacer
6146 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6148 ////////////Text style
6149 row = gtk_hbox_new (FALSE, 4);
6151 // bold
6152 rbutton = gtk_toggle_button_new ();
6153 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6154 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6155 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6156 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6158 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6159 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6160 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6162 // italic
6163 rbutton = gtk_toggle_button_new ();
6164 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6165 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6166 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6167 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6169 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6170 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6171 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6173 gtk_toolbar_append_widget( tbl, row, "", "");
6175 //spacer
6176 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6178 ////////////Text orientation
6179 group = gtk_radio_button_new (NULL);
6180 row = gtk_hbox_new (FALSE, 4);
6181 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6183 // horizontal
6184 rbutton = group;
6185 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6186 gtk_container_add (GTK_CONTAINER (rbutton),
6187 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6188 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6189 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6191 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6192 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6193 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6195 // vertical
6196 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6197 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6198 gtk_container_add (GTK_CONTAINER (rbutton),
6199 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6200 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6201 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6203 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6204 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6205 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6206 gtk_toolbar_append_widget( tbl, row, "", "" );
6209 //watch selection
6210 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6212 sigc::connection *c_selection_changed =
6213 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6214 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6215 pool->add_connection ("selection-changed", c_selection_changed);
6217 sigc::connection *c_selection_modified =
6218 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6219 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6220 pool->add_connection ("selection-modified", c_selection_modified);
6222 sigc::connection *c_subselection_changed =
6223 new sigc::connection (desktop->connectToolSubselectionChanged
6224 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6225 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6227 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6230 gtk_widget_show_all( GTK_WIDGET(tbl) );
6232 return GTK_WIDGET(tbl);
6233 } // end of sp_text_toolbox_new()
6235 }//<unnamed> namespace
6238 //#########################
6239 //## Connector ##
6240 //#########################
6242 static void sp_connector_path_set_avoid(void)
6243 {
6244 cc_selection_set_avoid(true);
6245 }
6248 static void sp_connector_path_set_ignore(void)
6249 {
6250 cc_selection_set_avoid(false);
6251 }
6255 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6256 {
6257 // quit if run by the _changed callbacks
6258 if (g_object_get_data( tbl, "freeze" )) {
6259 return;
6260 }
6262 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6263 SPDocument *doc = sp_desktop_document(desktop);
6265 if (!sp_document_get_undo_sensitive(doc))
6266 {
6267 return;
6268 }
6270 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6272 if ( repr->attribute("inkscape:connector-spacing") ) {
6273 gdouble priorValue = gtk_adjustment_get_value(adj);
6274 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6275 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6276 return;
6277 }
6278 } else if ( adj->value == defaultConnSpacing ) {
6279 return;
6280 }
6282 // in turn, prevent callbacks from responding
6283 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6285 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6286 SP_OBJECT(desktop->namedview)->updateRepr();
6288 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6289 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6290 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6291 NR::Matrix m = NR::identity();
6292 avoid_item_move(&m, item);
6293 }
6295 if (items) {
6296 g_slist_free(items);
6297 }
6299 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6300 _("Change connector spacing"));
6302 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6304 spinbutton_defocus(GTK_OBJECT(tbl));
6305 }
6307 static void sp_connector_graph_layout(void)
6308 {
6309 if (!SP_ACTIVE_DESKTOP) return;
6311 // hack for clones, see comment in align-and-distribute.cpp
6312 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6313 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6315 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6317 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6319 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6320 }
6322 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6323 {
6324 if ( gtk_toggle_action_get_active( act ) ) {
6325 prefs_set_string_attribute("tools.connector", "directedlayout",
6326 "true");
6327 } else {
6328 prefs_set_string_attribute("tools.connector", "directedlayout",
6329 "false");
6330 }
6331 }
6333 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6334 {
6335 if ( gtk_toggle_action_get_active( act ) ) {
6336 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6337 "true");
6338 } else {
6339 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6340 "false");
6341 }
6342 }
6345 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6346 {
6347 prefs_set_double_attribute("tools.connector", "length", adj->value);
6348 spinbutton_defocus(GTK_OBJECT(tbl));
6349 }
6351 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6352 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6353 bool /*is_interactive*/, gpointer data)
6354 {
6355 GtkWidget *tbl = GTK_WIDGET(data);
6357 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6358 return;
6359 }
6360 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6361 return;
6362 }
6364 GtkAdjustment *adj = (GtkAdjustment*)
6365 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6366 gdouble spacing = defaultConnSpacing;
6367 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6369 gtk_adjustment_set_value(adj, spacing);
6370 }
6373 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6374 NULL, /* child_added */
6375 NULL, /* child_removed */
6376 connector_tb_event_attr_changed,
6377 NULL, /* content_changed */
6378 NULL /* order_changed */
6379 };
6382 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6383 {
6384 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6386 {
6387 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6388 _("Avoid"),
6389 _("Make connectors avoid selected objects"),
6390 "connector_avoid",
6391 secondarySize );
6392 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6393 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6394 }
6396 {
6397 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6398 _("Ignore"),
6399 _("Make connectors ignore selected objects"),
6400 "connector_ignore",
6401 secondarySize );
6402 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6403 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6404 }
6406 EgeAdjustmentAction* eact = 0;
6408 // Spacing spinbox
6409 eact = create_adjustment_action( "ConnectorSpacingAction",
6410 _("Connector Spacing"), _("Spacing:"),
6411 _("The amount of space left around objects by auto-routing connectors"),
6412 "tools.connector", "spacing", defaultConnSpacing,
6413 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6414 0, 100, 1.0, 10.0,
6415 0, 0, 0,
6416 connector_spacing_changed, 1, 0 );
6417 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6419 // Graph (connector network) layout
6420 {
6421 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6422 _("Graph"),
6423 _("Nicely arrange selected connector network"),
6424 "graph_layout",
6425 secondarySize );
6426 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6427 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6428 }
6430 // Default connector length spinbox
6431 eact = create_adjustment_action( "ConnectorLengthAction",
6432 _("Connector Length"), _("Length:"),
6433 _("Ideal length for connectors when layout is applied"),
6434 "tools.connector", "length", 100,
6435 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6436 10, 1000, 10.0, 100.0,
6437 0, 0, 0,
6438 connector_length_changed, 1, 0 );
6439 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6442 // Directed edges toggle button
6443 {
6444 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6445 _("Downwards"),
6446 _("Make connectors with end-markers (arrows) point downwards"),
6447 "directed_graph",
6448 Inkscape::ICON_SIZE_DECORATION );
6449 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6451 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6452 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6453 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6455 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6456 }
6458 // Avoid overlaps toggle button
6459 {
6460 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6461 _("Remove overlaps"),
6462 _("Do not allow overlapping shapes"),
6463 "remove_overlaps",
6464 Inkscape::ICON_SIZE_DECORATION );
6465 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6467 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6468 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6469 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6471 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6472 }
6474 // Code to watch for changes to the connector-spacing attribute in
6475 // the XML.
6476 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6477 g_assert(repr != NULL);
6479 purge_repr_listener( holder, holder );
6481 if (repr) {
6482 g_object_set_data( holder, "repr", repr );
6483 Inkscape::GC::anchor(repr);
6484 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6485 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6486 }
6487 } // end of sp_connector_toolbox_prep()
6490 //#########################
6491 //## Paintbucket ##
6492 //#########################
6494 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6495 {
6496 gint channels = ege_select_one_action_get_active( act );
6497 flood_channels_set_channels( channels );
6498 }
6500 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6501 {
6502 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6503 }
6505 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6506 {
6507 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6508 }
6510 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6511 {
6512 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6513 SPUnit const *unit = tracker->getActiveUnit();
6515 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6517 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6518 }
6520 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6521 {
6522 // FIXME: make defaults settable via Inkscape Options
6523 struct KeyValue {
6524 char const *key;
6525 double value;
6526 } const key_values[] = {
6527 {"threshold", 15},
6528 {"offset", 0.0}
6529 };
6531 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6532 KeyValue const &kv = key_values[i];
6533 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6534 if ( adj ) {
6535 gtk_adjustment_set_value(adj, kv.value);
6536 }
6537 }
6539 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6540 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6541 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6542 ege_select_one_action_set_active( autogap_action, 0 );
6543 }
6545 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6546 {
6547 EgeAdjustmentAction* eact = 0;
6549 {
6550 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6552 GList* items = 0;
6553 gint count = 0;
6554 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6555 {
6556 GtkTreeIter iter;
6557 gtk_list_store_append( model, &iter );
6558 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6559 count++;
6560 }
6561 g_list_free( items );
6562 items = 0;
6563 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6564 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6565 ege_select_one_action_set_appearance( act1, "compact" );
6566 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6567 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6568 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6569 g_object_set_data( holder, "channels_action", act1 );
6570 }
6572 // Spacing spinbox
6573 {
6574 eact = create_adjustment_action(
6575 "ThresholdAction",
6576 _("Fill Threshold"), _("Threshold:"),
6577 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6578 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6579 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6580 0, 0, 0,
6581 paintbucket_threshold_changed, 1, 0 );
6583 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6584 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6585 }
6587 // Create the units menu.
6588 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6589 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6590 if (stored_unit)
6591 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6592 g_object_set_data( holder, "tracker", tracker );
6593 {
6594 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6595 gtk_action_group_add_action( mainActions, act );
6596 }
6598 // Offset spinbox
6599 {
6600 eact = create_adjustment_action(
6601 "OffsetAction",
6602 _("Grow/shrink by"), _("Grow/shrink by:"),
6603 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6604 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6605 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6606 0, 0, 0,
6607 paintbucket_offset_changed, 1, 2);
6608 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6610 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6611 }
6613 /* Auto Gap */
6614 {
6615 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6617 GList* items = 0;
6618 gint count = 0;
6619 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6620 {
6621 GtkTreeIter iter;
6622 gtk_list_store_append( model, &iter );
6623 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6624 count++;
6625 }
6626 g_list_free( items );
6627 items = 0;
6628 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6629 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6630 ege_select_one_action_set_appearance( act2, "compact" );
6631 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6632 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6633 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6634 g_object_set_data( holder, "autogap_action", act2 );
6635 }
6637 /* Reset */
6638 {
6639 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6640 _("Defaults"),
6641 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6642 GTK_STOCK_CLEAR );
6643 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6644 gtk_action_group_add_action( mainActions, act );
6645 gtk_action_set_sensitive( act, TRUE );
6646 }
6648 }
6650 /*
6651 Local Variables:
6652 mode:c++
6653 c-file-style:"stroustrup"
6654 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6655 indent-tabs-mode:nil
6656 fill-column:99
6657 End:
6658 */
6659 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :