5a967a1c438dbe2c8c6ae42581bf3653e179d3f1
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 " </toolbar>"
454 " <toolbar name='DropperToolbar'>"
455 " <toolitem action='DropperOpacityAction' />"
456 " <toolitem action='DropperPickAlphaAction' />"
457 " <toolitem action='DropperSetAlphaAction' />"
458 " </toolbar>"
460 " <toolbar name='ConnectorToolbar'>"
461 " <toolitem action='ConnectorAvoidAction' />"
462 " <toolitem action='ConnectorIgnoreAction' />"
463 " <toolitem action='ConnectorSpacingAction' />"
464 " <toolitem action='ConnectorGraphAction' />"
465 " <toolitem action='ConnectorLengthAction' />"
466 " <toolitem action='ConnectorDirectedAction' />"
467 " <toolitem action='ConnectorOverlapAction' />"
468 " </toolbar>"
470 "</ui>"
471 ;
473 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
475 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
477 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
478 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
480 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
481 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
484 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
487 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
488 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
489 Inkscape::UI::View::View *view, GtkTooltips *tt);
491 class VerbAction : public Gtk::Action {
492 public:
493 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
495 virtual ~VerbAction();
496 virtual void set_active(bool active = true);
498 protected:
499 virtual Gtk::Widget* create_menu_item_vfunc();
500 virtual Gtk::Widget* create_tool_item_vfunc();
502 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
503 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
505 virtual void on_activate();
507 private:
508 Inkscape::Verb* verb;
509 Inkscape::Verb* verb2;
510 Inkscape::UI::View::View *view;
511 GtkTooltips *tooltips;
512 bool active;
514 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
515 };
518 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
519 {
520 Glib::RefPtr<VerbAction> result;
521 SPAction *action = verb->get_action(view);
522 if ( action ) {
523 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
524 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
525 }
527 return result;
528 }
530 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
531 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
532 verb(verb),
533 verb2(verb2),
534 view(view),
535 tooltips(tooltips),
536 active(false)
537 {
538 }
540 VerbAction::~VerbAction()
541 {
542 }
544 Gtk::Widget* VerbAction::create_menu_item_vfunc()
545 {
546 // First call in to get the icon rendered if present in SVG
547 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
548 delete widget;
549 widget = 0;
551 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
552 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
553 return widg;
554 }
556 Gtk::Widget* VerbAction::create_tool_item_vfunc()
557 {
558 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
559 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
560 GtkWidget* toolbox = 0;
561 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
562 SP_BUTTON_TYPE_TOGGLE,
563 verb,
564 verb2,
565 view,
566 tooltips );
567 if ( active ) {
568 sp_button_toggle_set_down( SP_BUTTON(button), active);
569 }
570 gtk_widget_show_all( button );
571 Gtk::Widget* wrapped = Glib::wrap(button);
572 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
573 holder->add(*wrapped);
575 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
576 return holder;
577 }
579 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
580 {
581 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
582 Gtk::Action::connect_proxy_vfunc(proxy);
583 }
585 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
586 {
587 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
588 Gtk::Action::disconnect_proxy_vfunc(proxy);
589 }
591 void VerbAction::set_active(bool active)
592 {
593 this->active = active;
594 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
595 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
596 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
597 if (ti) {
598 // *should* have one child that is the SPButton
599 Gtk::Widget* child = ti->get_child();
600 if ( child && SP_IS_BUTTON(child->gobj()) ) {
601 SPButton* button = SP_BUTTON(child->gobj());
602 sp_button_toggle_set_down( button, active );
603 }
604 }
605 }
606 }
608 void VerbAction::on_activate()
609 {
610 if ( verb ) {
611 SPAction *action = verb->get_action(view);
612 if ( action ) {
613 sp_action_perform(action, 0);
614 }
615 }
616 }
618 /* Global text entry widgets necessary for update */
619 /* GtkWidget *dropper_rgb_entry,
620 *dropper_opacity_entry ; */
621 // should be made a private member once this is converted to class
623 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
624 connection->disconnect();
625 delete connection;
626 }
628 static void purge_repr_listener( GObject* obj, GObject* tbl )
629 {
630 (void)obj;
631 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
632 if (oldrepr) { // remove old listener
633 sp_repr_remove_listener_by_data(oldrepr, tbl);
634 Inkscape::GC::release(oldrepr);
635 oldrepr = 0;
636 g_object_set_data( tbl, "repr", NULL );
637 }
638 }
640 GtkWidget *
641 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
642 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
643 Inkscape::UI::View::View *view, GtkTooltips *tt)
644 {
645 SPAction *action = verb->get_action(view);
646 if (!action) return NULL;
648 SPAction *doubleclick_action;
649 if (doubleclick_verb)
650 doubleclick_action = doubleclick_verb->get_action(view);
651 else
652 doubleclick_action = NULL;
654 /* fixme: Handle sensitive/unsensitive */
655 /* fixme: Implement sp_button_new_from_action */
656 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
657 gtk_widget_show(b);
660 unsigned int shortcut = sp_shortcut_get_primary(verb);
661 if (shortcut) {
662 gchar key[256];
663 sp_ui_shortcut_string(shortcut, key);
664 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
665 if ( t ) {
666 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
667 }
668 g_free(tip);
669 } else {
670 if ( t ) {
671 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
672 }
673 }
675 return b;
676 }
679 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
680 {
681 SPAction* targetAction = SP_ACTION(user_data);
682 if ( targetAction ) {
683 sp_action_perform( targetAction, NULL );
684 }
685 }
687 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
688 {
689 if ( data ) {
690 GtkAction* act = GTK_ACTION(data);
691 gtk_action_set_sensitive( act, sensitive );
692 }
693 }
695 static SPActionEventVector action_event_vector = {
696 {NULL},
697 NULL,
698 NULL,
699 sp_action_action_set_sensitive,
700 NULL,
701 NULL
702 };
704 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
705 {
706 GtkAction* act = 0;
708 SPAction* targetAction = verb->get_action(view);
709 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
710 act = GTK_ACTION(inky);
711 gtk_action_set_sensitive( act, targetAction->sensitive );
713 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
715 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
716 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
718 return act;
719 }
721 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
722 {
723 Inkscape::UI::View::View *view = desktop;
724 gint verbsToUse[] = {
725 // disabled until we have icons for them:
726 //find
727 //SP_VERB_EDIT_TILE,
728 //SP_VERB_EDIT_UNTILE,
729 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
730 SP_VERB_DIALOG_DISPLAY,
731 SP_VERB_DIALOG_FILL_STROKE,
732 SP_VERB_DIALOG_NAMEDVIEW,
733 SP_VERB_DIALOG_TEXT,
734 SP_VERB_DIALOG_XML_EDITOR,
735 SP_VERB_EDIT_CLONE,
736 SP_VERB_EDIT_COPY,
737 SP_VERB_EDIT_CUT,
738 SP_VERB_EDIT_DUPLICATE,
739 SP_VERB_EDIT_PASTE,
740 SP_VERB_EDIT_REDO,
741 SP_VERB_EDIT_UNDO,
742 SP_VERB_EDIT_UNLINK_CLONE,
743 SP_VERB_FILE_EXPORT,
744 SP_VERB_FILE_IMPORT,
745 SP_VERB_FILE_NEW,
746 SP_VERB_FILE_OPEN,
747 SP_VERB_FILE_PRINT,
748 SP_VERB_FILE_SAVE,
749 SP_VERB_OBJECT_TO_CURVE,
750 SP_VERB_SELECTION_GROUP,
751 SP_VERB_SELECTION_OUTLINE,
752 SP_VERB_SELECTION_UNGROUP,
753 SP_VERB_ZOOM_1_1,
754 SP_VERB_ZOOM_1_2,
755 SP_VERB_ZOOM_2_1,
756 SP_VERB_ZOOM_DRAWING,
757 SP_VERB_ZOOM_IN,
758 SP_VERB_ZOOM_NEXT,
759 SP_VERB_ZOOM_OUT,
760 SP_VERB_ZOOM_PAGE,
761 SP_VERB_ZOOM_PAGE_WIDTH,
762 SP_VERB_ZOOM_PREV,
763 SP_VERB_ZOOM_SELECTION,
764 };
766 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
768 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
769 Glib::RefPtr<Gtk::ActionGroup> mainActions;
770 if ( groups.find(desktop) != groups.end() ) {
771 mainActions = groups[desktop];
772 }
774 if ( !mainActions ) {
775 mainActions = Gtk::ActionGroup::create("main");
776 groups[desktop] = mainActions;
777 }
779 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
780 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
781 if ( verb ) {
782 if (!mainActions->get_action(verb->get_id())) {
783 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
784 mainActions->add(Glib::wrap(act));
785 }
786 }
787 }
789 if ( !mainActions->get_action("ToolZoom") ) {
790 GtkTooltips *tt = gtk_tooltips_new();
791 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
792 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
793 if ( va ) {
794 mainActions->add(va);
795 if ( i == 0 ) {
796 va->set_active(true);
797 }
798 }
799 }
800 }
803 return mainActions;
804 }
807 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
808 {
809 gtk_widget_set_size_request( widget,
810 widget->allocation.width,
811 widget->allocation.height );
812 }
814 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
815 {
816 gtk_widget_set_size_request( widget, -1, -1 );
817 }
821 GtkWidget *
822 sp_tool_toolbox_new()
823 {
824 GtkTooltips *tt = gtk_tooltips_new();
825 GtkWidget* tb = gtk_toolbar_new();
826 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
827 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
829 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
830 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
832 gtk_widget_set_sensitive(tb, FALSE);
834 GtkWidget *hb = gtk_handle_box_new();
835 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
836 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
837 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
839 gtk_container_add(GTK_CONTAINER(hb), tb);
840 gtk_widget_show(GTK_WIDGET(tb));
842 sigc::connection* conn = new sigc::connection;
843 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
845 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
846 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
848 return hb;
849 }
851 GtkWidget *
852 sp_aux_toolbox_new()
853 {
854 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
856 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
858 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
860 gtk_widget_set_sensitive(tb, FALSE);
862 GtkWidget *hb = gtk_handle_box_new();
863 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
864 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
865 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
867 gtk_container_add(GTK_CONTAINER(hb), tb);
868 gtk_widget_show(GTK_WIDGET(tb));
870 sigc::connection* conn = new sigc::connection;
871 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
873 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
874 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
876 return hb;
877 }
879 //####################################
880 //# Commands Bar
881 //####################################
883 GtkWidget *
884 sp_commands_toolbox_new()
885 {
886 GtkWidget *tb = gtk_toolbar_new();
888 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
889 gtk_widget_set_sensitive(tb, FALSE);
891 GtkWidget *hb = gtk_handle_box_new();
892 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
893 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
894 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
896 gtk_container_add(GTK_CONTAINER(hb), tb);
897 gtk_widget_show(GTK_WIDGET(tb));
899 sigc::connection* conn = new sigc::connection;
900 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
902 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
903 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
905 return hb;
906 }
909 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
910 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
911 gchar const *path, gchar const *data, gdouble def,
912 GtkWidget *focusTarget,
913 GtkWidget *us,
914 GObject *dataKludge,
915 gboolean altx, gchar const *altx_mark,
916 gdouble lower, gdouble upper, gdouble step, gdouble page,
917 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
918 void (*callback)(GtkAdjustment *, GObject *),
919 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
920 {
921 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
922 lower, upper, step, page, page ) );
923 if (us) {
924 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
925 }
927 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
929 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
930 if ( shortLabel ) {
931 g_object_set( act, "short_label", shortLabel, NULL );
932 }
934 if ( (descrCount > 0) && descrLabels && descrValues ) {
935 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
936 }
938 if ( focusTarget ) {
939 ege_adjustment_action_set_focuswidget( act, focusTarget );
940 }
942 if ( altx && altx_mark ) {
943 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
944 }
946 if ( dataKludge ) {
947 g_object_set_data( dataKludge, data, adj );
948 }
950 // Using a cast just to make sure we pass in the right kind of function pointer
951 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
953 return act;
954 }
957 //####################################
958 //# node editing callbacks
959 //####################################
961 /**
962 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
963 */
964 static ShapeEditor *get_current_shape_editor()
965 {
966 if (!SP_ACTIVE_DESKTOP) {
967 return NULL;
968 }
970 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
972 if (!SP_IS_NODE_CONTEXT(event_context)) {
973 return NULL;
974 }
976 return SP_NODE_CONTEXT(event_context)->shape_editor;
977 }
980 void
981 sp_node_path_edit_add(void)
982 {
983 ShapeEditor *shape_editor = get_current_shape_editor();
984 if (shape_editor) shape_editor->add_node();
985 }
987 void
988 sp_node_path_edit_delete(void)
989 {
990 ShapeEditor *shape_editor = get_current_shape_editor();
991 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
992 }
994 void
995 sp_node_path_edit_delete_segment(void)
996 {
997 ShapeEditor *shape_editor = get_current_shape_editor();
998 if (shape_editor) shape_editor->delete_segment();
999 }
1001 void
1002 sp_node_path_edit_break(void)
1003 {
1004 ShapeEditor *shape_editor = get_current_shape_editor();
1005 if (shape_editor) shape_editor->break_at_nodes();
1006 }
1008 void
1009 sp_node_path_edit_join(void)
1010 {
1011 ShapeEditor *shape_editor = get_current_shape_editor();
1012 if (shape_editor) shape_editor->join_nodes();
1013 }
1015 void
1016 sp_node_path_edit_join_segment(void)
1017 {
1018 ShapeEditor *shape_editor = get_current_shape_editor();
1019 if (shape_editor) shape_editor->join_segments();
1020 }
1022 void
1023 sp_node_path_edit_toline(void)
1024 {
1025 ShapeEditor *shape_editor = get_current_shape_editor();
1026 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1027 }
1029 void
1030 sp_node_path_edit_tocurve(void)
1031 {
1032 ShapeEditor *shape_editor = get_current_shape_editor();
1033 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1034 }
1036 void
1037 sp_node_path_edit_cusp(void)
1038 {
1039 ShapeEditor *shape_editor = get_current_shape_editor();
1040 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1041 }
1043 void
1044 sp_node_path_edit_smooth(void)
1045 {
1046 ShapeEditor *shape_editor = get_current_shape_editor();
1047 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1048 }
1050 void
1051 sp_node_path_edit_symmetrical(void)
1052 {
1053 ShapeEditor *shape_editor = get_current_shape_editor();
1054 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1055 }
1057 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1058 bool show = gtk_toggle_action_get_active( act );
1059 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1060 ShapeEditor *shape_editor = get_current_shape_editor();
1061 if (shape_editor) shape_editor->show_handles(show);
1062 }
1064 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1065 bool show = gtk_toggle_action_get_active( act );
1066 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1067 ShapeEditor *shape_editor = get_current_shape_editor();
1068 if (shape_editor) shape_editor->show_helperpath(show);
1069 }
1071 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1072 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1073 }
1075 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1076 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1077 }
1079 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1080 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1081 }
1083 /* is called when the node selection is modified */
1084 static void
1085 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1086 {
1087 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1088 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1089 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1090 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1092 // quit if run by the attr_changed listener
1093 if (g_object_get_data( tbl, "freeze" )) {
1094 return;
1095 }
1097 // in turn, prevent listener from responding
1098 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1100 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1101 SPUnit const *unit = tracker->getActiveUnit();
1103 ShapeEditor *shape_editor = get_current_shape_editor();
1104 if (shape_editor && shape_editor->has_nodepath()) {
1105 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1106 int n_selected = 0;
1107 if (nodepath) {
1108 n_selected = nodepath->numSelected();
1109 }
1111 if (n_selected == 0) {
1112 gtk_action_set_sensitive(xact, FALSE);
1113 gtk_action_set_sensitive(yact, FALSE);
1114 } else {
1115 gtk_action_set_sensitive(xact, TRUE);
1116 gtk_action_set_sensitive(yact, TRUE);
1117 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1118 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1120 if (n_selected == 1) {
1121 Geom::Point sel_node = nodepath->singleSelectedCoords();
1122 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1123 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1124 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1125 }
1126 } else {
1127 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1128 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1129 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1130 /* Note: Currently x and y will always have a value, even if the coordinates of the
1131 selected nodes don't coincide (in this case we use the coordinates of the center
1132 of the bounding box). So the entries are never set to zero. */
1133 // FIXME: Maybe we should clear the entry if several nodes are selected
1134 // instead of providing a kind of average value
1135 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1136 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1137 }
1138 }
1139 }
1140 } else {
1141 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1142 gtk_action_set_sensitive(xact, FALSE);
1143 gtk_action_set_sensitive(yact, FALSE);
1144 }
1146 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1147 }
1149 static void
1150 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1151 {
1152 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1154 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1155 SPUnit const *unit = tracker->getActiveUnit();
1157 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1158 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1159 }
1161 // quit if run by the attr_changed listener
1162 if (g_object_get_data( tbl, "freeze" )) {
1163 return;
1164 }
1166 // in turn, prevent listener from responding
1167 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1169 ShapeEditor *shape_editor = get_current_shape_editor();
1170 if (shape_editor && shape_editor->has_nodepath()) {
1171 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1172 if (!strcmp(value_name, "x")) {
1173 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1174 }
1175 if (!strcmp(value_name, "y")) {
1176 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1177 }
1178 }
1180 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1181 }
1183 static void
1184 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1185 {
1186 sp_node_path_value_changed(adj, tbl, "x");
1187 }
1189 static void
1190 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1191 {
1192 sp_node_path_value_changed(adj, tbl, "y");
1193 }
1195 void
1196 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1197 {
1198 {
1199 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1200 SPItem *item = selection->singleItem();
1201 if (item && SP_IS_LPE_ITEM(item)) {
1202 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1203 gtk_action_set_sensitive(w, TRUE);
1204 } else {
1205 gtk_action_set_sensitive(w, FALSE);
1206 }
1207 } else {
1208 gtk_action_set_sensitive(w, FALSE);
1209 }
1210 }
1212 {
1213 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1214 SPItem *item = selection->singleItem();
1215 if (item && item->clip_ref && item->clip_ref->getObject()) {
1216 gtk_action_set_sensitive(w, TRUE);
1217 } else {
1218 gtk_action_set_sensitive(w, FALSE);
1219 }
1220 }
1222 {
1223 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1224 SPItem *item = selection->singleItem();
1225 if (item && item->mask_ref && item->mask_ref->getObject()) {
1226 gtk_action_set_sensitive(w, TRUE);
1227 } else {
1228 gtk_action_set_sensitive(w, FALSE);
1229 }
1230 }
1231 }
1233 void
1234 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1235 {
1236 sp_node_toolbox_sel_changed (selection, tbl);
1237 }
1241 //################################
1242 //## Node Editing Toolbox ##
1243 //################################
1245 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1246 {
1247 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1248 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1249 g_object_set_data( holder, "tracker", tracker );
1251 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1253 {
1254 InkAction* inky = ink_action_new( "NodeInsertAction",
1255 _("Insert node"),
1256 _("Insert new nodes into selected segments"),
1257 "node_insert",
1258 secondarySize );
1259 g_object_set( inky, "short_label", _("Insert"), NULL );
1260 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1261 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1262 }
1264 {
1265 InkAction* inky = ink_action_new( "NodeDeleteAction",
1266 _("Delete node"),
1267 _("Delete selected nodes"),
1268 "node_delete",
1269 secondarySize );
1270 g_object_set( inky, "short_label", _("Delete"), NULL );
1271 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1272 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1273 }
1275 {
1276 InkAction* inky = ink_action_new( "NodeJoinAction",
1277 _("Join endnodes"),
1278 _("Join selected endnodes"),
1279 "node_join",
1280 secondarySize );
1281 g_object_set( inky, "short_label", _("Join"), NULL );
1282 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1283 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1284 }
1286 {
1287 InkAction* inky = ink_action_new( "NodeBreakAction",
1288 _("Break nodes"),
1289 _("Break path at selected nodes"),
1290 "node_break",
1291 secondarySize );
1292 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1293 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1294 }
1297 {
1298 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1299 _("Join with segment"),
1300 _("Join selected endnodes with a new segment"),
1301 "node_join_segment",
1302 secondarySize );
1303 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1304 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1305 }
1307 {
1308 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1309 _("Delete segment"),
1310 _("Delete segment between two non-endpoint nodes"),
1311 "node_delete_segment",
1312 secondarySize );
1313 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1314 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1315 }
1317 {
1318 InkAction* inky = ink_action_new( "NodeCuspAction",
1319 _("Node Cusp"),
1320 _("Make selected nodes corner"),
1321 "node_cusp",
1322 secondarySize );
1323 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1324 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1325 }
1327 {
1328 InkAction* inky = ink_action_new( "NodeSmoothAction",
1329 _("Node Smooth"),
1330 _("Make selected nodes smooth"),
1331 "node_smooth",
1332 secondarySize );
1333 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1334 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1335 }
1337 {
1338 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1339 _("Node Symmetric"),
1340 _("Make selected nodes symmetric"),
1341 "node_symmetric",
1342 secondarySize );
1343 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1344 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1345 }
1347 {
1348 InkAction* inky = ink_action_new( "NodeLineAction",
1349 _("Node Line"),
1350 _("Make selected segments lines"),
1351 "node_line",
1352 secondarySize );
1353 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1354 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1355 }
1357 {
1358 InkAction* inky = ink_action_new( "NodeCurveAction",
1359 _("Node Curve"),
1360 _("Make selected segments curves"),
1361 "node_curve",
1362 secondarySize );
1363 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1364 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365 }
1367 {
1368 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1369 _("Show Handles"),
1370 _("Show the Bezier handles of selected nodes"),
1371 "nodes_show_handles",
1372 Inkscape::ICON_SIZE_DECORATION );
1373 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1374 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1375 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1376 }
1378 {
1379 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1380 _("Show Outline"),
1381 _("Show the outline of the path"),
1382 "nodes_show_helperpath",
1383 Inkscape::ICON_SIZE_DECORATION );
1384 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1385 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1386 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1387 }
1389 {
1390 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1391 _("Next path effect parameter"),
1392 _("Show next path effect parameter for editing"),
1393 "edit_next_parameter",
1394 Inkscape::ICON_SIZE_DECORATION );
1395 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1396 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1397 g_object_set_data( holder, "nodes_lpeedit", inky);
1398 }
1400 {
1401 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1402 _("Edit clipping path"),
1403 _("Edit the clipping path of the object"),
1404 "nodeedit-clippath",
1405 Inkscape::ICON_SIZE_DECORATION );
1406 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1407 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408 g_object_set_data( holder, "nodes_clippathedit", inky);
1409 }
1411 {
1412 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1413 _("Edit mask path"),
1414 _("Edit the mask of the object"),
1415 "nodeedit-mask",
1416 Inkscape::ICON_SIZE_DECORATION );
1417 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1418 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1419 g_object_set_data( holder, "nodes_maskedit", inky);
1420 }
1422 /* X coord of selected node(s) */
1423 {
1424 EgeAdjustmentAction* eact = 0;
1425 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1426 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1427 eact = create_adjustment_action( "NodeXAction",
1428 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1429 "tools.nodes", "Xcoord", 0,
1430 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1431 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1432 labels, values, G_N_ELEMENTS(labels),
1433 sp_node_path_x_value_changed );
1434 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1435 g_object_set_data( holder, "nodes_x_action", eact );
1436 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1437 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1438 }
1440 /* Y coord of selected node(s) */
1441 {
1442 EgeAdjustmentAction* eact = 0;
1443 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1444 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1445 eact = create_adjustment_action( "NodeYAction",
1446 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1447 "tools.nodes", "Ycoord", 0,
1448 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1449 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1450 labels, values, G_N_ELEMENTS(labels),
1451 sp_node_path_y_value_changed );
1452 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1453 g_object_set_data( holder, "nodes_y_action", eact );
1454 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1455 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1456 }
1458 // add the units menu
1459 {
1460 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1461 gtk_action_group_add_action( mainActions, act );
1462 }
1465 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1467 //watch selection
1468 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1470 sigc::connection *c_selection_changed =
1471 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1472 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1473 pool->add_connection ("selection-changed", c_selection_changed);
1475 sigc::connection *c_selection_modified =
1476 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1477 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1478 pool->add_connection ("selection-modified", c_selection_modified);
1480 sigc::connection *c_subselection_changed =
1481 new sigc::connection (desktop->connectToolSubselectionChanged
1482 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1483 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1485 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1487 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1488 } // end of sp_node_toolbox_prep()
1491 //########################
1492 //## Zoom Toolbox ##
1493 //########################
1495 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1496 {
1497 // no custom GtkAction setup needed
1498 } // end of sp_zoom_toolbox_prep()
1500 void
1501 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1502 {
1503 toolbox_set_desktop(toolbox,
1504 desktop,
1505 setup_tool_toolbox,
1506 update_tool_toolbox,
1507 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1508 "event_context_connection")));
1509 }
1512 void
1513 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1514 {
1515 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1516 desktop,
1517 setup_aux_toolbox,
1518 update_aux_toolbox,
1519 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1520 "event_context_connection")));
1521 }
1523 void
1524 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1525 {
1526 toolbox_set_desktop(toolbox,
1527 desktop,
1528 setup_commands_toolbox,
1529 update_commands_toolbox,
1530 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1531 "event_context_connection")));
1532 }
1534 static void
1535 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1536 {
1537 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1538 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1540 if (old_desktop) {
1541 GList *children, *iter;
1543 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1544 for ( iter = children ; iter ; iter = iter->next ) {
1545 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1546 }
1547 g_list_free(children);
1548 }
1550 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1552 if (desktop) {
1553 gtk_widget_set_sensitive(toolbox, TRUE);
1554 setup_func(toolbox, desktop);
1555 update_func(desktop, desktop->event_context, toolbox);
1556 *conn = desktop->connectEventContextChanged
1557 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1558 } else {
1559 gtk_widget_set_sensitive(toolbox, FALSE);
1560 }
1562 } // end of toolbox_set_desktop()
1565 static void
1566 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1567 {
1568 gchar const * descr =
1569 "<ui>"
1570 " <toolbar name='ToolToolbar'>"
1571 " <toolitem action='ToolSelector' />"
1572 " <toolitem action='ToolNode' />"
1573 " <toolitem action='ToolTweak' />"
1574 " <toolitem action='ToolZoom' />"
1575 " <toolitem action='ToolRect' />"
1576 " <toolitem action='Tool3DBox' />"
1577 " <toolitem action='ToolArc' />"
1578 " <toolitem action='ToolStar' />"
1579 " <toolitem action='ToolSpiral' />"
1580 " <toolitem action='ToolPencil' />"
1581 " <toolitem action='ToolPen' />"
1582 " <toolitem action='ToolCalligraphic' />"
1583 " <toolitem action='ToolEraser' />"
1584 // " <toolitem action='ToolLPETool' />"
1585 " <toolitem action='ToolPaintBucket' />"
1586 " <toolitem action='ToolText' />"
1587 " <toolitem action='ToolConnector' />"
1588 " <toolitem action='ToolGradient' />"
1589 " <toolitem action='ToolDropper' />"
1590 " </toolbar>"
1591 "</ui>";
1592 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1593 GtkUIManager* mgr = gtk_ui_manager_new();
1594 GError* errVal = 0;
1596 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1597 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1599 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1600 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1601 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1602 }
1603 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1604 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1606 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1607 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1609 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1611 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1612 if ( child ) {
1613 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1614 }
1616 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1617 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1618 }
1621 static void
1622 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1623 {
1624 gchar const *const tname = ( eventcontext
1625 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1626 : NULL );
1627 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1629 for (int i = 0 ; tools[i].type_name ; i++ ) {
1630 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1631 if ( act ) {
1632 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1633 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1634 if ( verbAct ) {
1635 verbAct->set_active(setActive);
1636 }
1637 }
1638 }
1639 }
1641 static void
1642 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1643 {
1644 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1645 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1646 GtkUIManager* mgr = gtk_ui_manager_new();
1647 GError* errVal = 0;
1648 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1649 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1651 std::map<std::string, GtkWidget*> dataHolders;
1653 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1654 if ( aux_toolboxes[i].prep_func ) {
1655 // converted to GtkActions and UIManager
1657 GtkWidget* kludge = gtk_toolbar_new();
1658 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1659 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1660 dataHolders[aux_toolboxes[i].type_name] = kludge;
1661 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1662 } else {
1664 GtkWidget *sub_toolbox = 0;
1665 if (aux_toolboxes[i].create_func == NULL)
1666 sub_toolbox = sp_empty_toolbox_new(desktop);
1667 else {
1668 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1669 }
1671 gtk_size_group_add_widget( grouper, sub_toolbox );
1673 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1674 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1676 }
1677 }
1679 // Second pass to create toolbars *after* all GtkActions are created
1680 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1681 if ( aux_toolboxes[i].prep_func ) {
1682 // converted to GtkActions and UIManager
1684 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1686 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1687 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1689 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1690 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1691 g_free( tmp );
1692 tmp = 0;
1694 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1695 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1696 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1697 }
1698 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1701 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1703 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1704 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1705 swatch->setDesktop( desktop );
1706 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1707 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1708 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1709 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 );
1710 }
1712 gtk_widget_show_all( holder );
1713 sp_set_font_size_smaller( holder );
1715 gtk_size_group_add_widget( grouper, holder );
1717 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1718 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1719 }
1720 }
1722 g_object_unref( G_OBJECT(grouper) );
1723 }
1725 static void
1726 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1727 {
1728 gchar const *tname = ( eventcontext
1729 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1730 : NULL );
1731 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1732 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1733 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1734 gtk_widget_show_all(sub_toolbox);
1735 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1736 } else {
1737 gtk_widget_hide(sub_toolbox);
1738 }
1739 }
1740 }
1742 static void
1743 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1744 {
1745 gchar const * descr =
1746 "<ui>"
1747 " <toolbar name='CommandsToolbar'>"
1748 " <toolitem action='FileNew' />"
1749 " <toolitem action='FileOpen' />"
1750 " <toolitem action='FileSave' />"
1751 " <toolitem action='FilePrint' />"
1752 " <separator />"
1753 " <toolitem action='FileImport' />"
1754 " <toolitem action='FileExport' />"
1755 " <separator />"
1756 " <toolitem action='EditUndo' />"
1757 " <toolitem action='EditRedo' />"
1758 " <separator />"
1759 " <toolitem action='EditCopy' />"
1760 " <toolitem action='EditCut' />"
1761 " <toolitem action='EditPaste' />"
1762 " <separator />"
1763 " <toolitem action='ZoomSelection' />"
1764 " <toolitem action='ZoomDrawing' />"
1765 " <toolitem action='ZoomPage' />"
1766 " <separator />"
1767 " <toolitem action='EditDuplicate' />"
1768 " <toolitem action='EditClone' />"
1769 " <toolitem action='EditUnlinkClone' />"
1770 " <separator />"
1771 " <toolitem action='SelectionGroup' />"
1772 " <toolitem action='SelectionUnGroup' />"
1773 " <separator />"
1774 " <toolitem action='DialogFillStroke' />"
1775 " <toolitem action='DialogText' />"
1776 " <toolitem action='DialogXMLEditor' />"
1777 " <toolitem action='DialogAlignDistribute' />"
1778 " <separator />"
1779 " <toolitem action='DialogPreferences' />"
1780 " <toolitem action='DialogDocumentProperties' />"
1781 " </toolbar>"
1782 "</ui>";
1783 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1786 GtkUIManager* mgr = gtk_ui_manager_new();
1787 GError* errVal = 0;
1789 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1790 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1792 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1793 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1794 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1795 }
1797 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1798 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1800 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1801 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1804 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1806 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1807 if ( child ) {
1808 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1809 }
1811 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1812 }
1814 static void
1815 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1816 {
1817 }
1819 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1820 {
1821 gtk_widget_show(toolbox_toplevel);
1822 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1824 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1825 if (!shown_toolbox) {
1826 return;
1827 }
1828 gtk_widget_show(toolbox);
1830 gtk_widget_show_all(shown_toolbox);
1831 }
1833 static GtkWidget *
1834 sp_empty_toolbox_new(SPDesktop *desktop)
1835 {
1836 GtkWidget *tbl = gtk_toolbar_new();
1837 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1838 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1840 gtk_widget_show_all(tbl);
1841 sp_set_font_size_smaller (tbl);
1843 return tbl;
1844 }
1846 #define MODE_LABEL_WIDTH 70
1848 //########################
1849 //## Star ##
1850 //########################
1852 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1853 {
1854 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1856 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1857 // do not remember prefs if this call is initiated by an undo change, because undoing object
1858 // creation sets bogus values to its attributes before it is deleted
1859 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1860 }
1862 // quit if run by the attr_changed listener
1863 if (g_object_get_data( dataKludge, "freeze" )) {
1864 return;
1865 }
1867 // in turn, prevent listener from responding
1868 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1870 bool modmade = false;
1872 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1873 GSList const *items = selection->itemList();
1874 for (; items != NULL; items = items->next) {
1875 if (SP_IS_STAR((SPItem *) items->data)) {
1876 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1877 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1878 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1879 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1880 + M_PI / (gint)adj->value));
1881 SP_OBJECT((SPItem *) items->data)->updateRepr();
1882 modmade = true;
1883 }
1884 }
1885 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1886 _("Star: Change number of corners"));
1888 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1889 }
1891 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1892 {
1893 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1895 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1896 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1897 }
1899 // quit if run by the attr_changed listener
1900 if (g_object_get_data( dataKludge, "freeze" )) {
1901 return;
1902 }
1904 // in turn, prevent listener from responding
1905 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1907 bool modmade = false;
1908 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1909 GSList const *items = selection->itemList();
1910 for (; items != NULL; items = items->next) {
1911 if (SP_IS_STAR((SPItem *) items->data)) {
1912 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1914 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1915 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1916 if (r2 < r1) {
1917 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1918 } else {
1919 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1920 }
1922 SP_OBJECT((SPItem *) items->data)->updateRepr();
1923 modmade = true;
1924 }
1925 }
1927 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1928 _("Star: Change spoke ratio"));
1930 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1931 }
1933 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1934 {
1935 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1936 bool flat = ege_select_one_action_get_active( act ) == 0;
1938 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1939 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1940 flat ? "true" : "false" );
1941 }
1943 // quit if run by the attr_changed listener
1944 if (g_object_get_data( dataKludge, "freeze" )) {
1945 return;
1946 }
1948 // in turn, prevent listener from responding
1949 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1951 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1952 GSList const *items = selection->itemList();
1953 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1954 bool modmade = false;
1956 if ( prop_action ) {
1957 gtk_action_set_sensitive( prop_action, !flat );
1958 }
1960 for (; items != NULL; items = items->next) {
1961 if (SP_IS_STAR((SPItem *) items->data)) {
1962 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1963 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1964 SP_OBJECT((SPItem *) items->data)->updateRepr();
1965 modmade = true;
1966 }
1967 }
1969 if (modmade) {
1970 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1971 flat ? _("Make polygon") : _("Make star"));
1972 }
1974 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1975 }
1977 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1978 {
1979 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1981 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1982 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1983 }
1985 // quit if run by the attr_changed listener
1986 if (g_object_get_data( dataKludge, "freeze" )) {
1987 return;
1988 }
1990 // in turn, prevent listener from responding
1991 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1993 bool modmade = false;
1995 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1996 GSList const *items = selection->itemList();
1997 for (; items != NULL; items = items->next) {
1998 if (SP_IS_STAR((SPItem *) items->data)) {
1999 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2000 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2001 SP_OBJECT(items->data)->updateRepr();
2002 modmade = true;
2003 }
2004 }
2005 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2006 _("Star: Change rounding"));
2008 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2009 }
2011 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2012 {
2013 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2015 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2016 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2017 }
2019 // quit if run by the attr_changed listener
2020 if (g_object_get_data( dataKludge, "freeze" )) {
2021 return;
2022 }
2024 // in turn, prevent listener from responding
2025 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2027 bool modmade = false;
2029 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2030 GSList const *items = selection->itemList();
2031 for (; items != NULL; items = items->next) {
2032 if (SP_IS_STAR((SPItem *) items->data)) {
2033 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2034 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2035 SP_OBJECT(items->data)->updateRepr();
2036 modmade = true;
2037 }
2038 }
2039 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2040 _("Star: Change randomization"));
2042 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2043 }
2046 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2047 gchar const */*old_value*/, gchar const */*new_value*/,
2048 bool /*is_interactive*/, gpointer data)
2049 {
2050 GtkWidget *tbl = GTK_WIDGET(data);
2052 // quit if run by the _changed callbacks
2053 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2054 return;
2055 }
2057 // in turn, prevent callbacks from responding
2058 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2060 GtkAdjustment *adj = 0;
2062 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2063 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2065 if (!strcmp(name, "inkscape:randomized")) {
2066 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2067 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2068 } else if (!strcmp(name, "inkscape:rounded")) {
2069 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2070 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2071 } else if (!strcmp(name, "inkscape:flatsided")) {
2072 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2073 char const *flatsides = repr->attribute("inkscape:flatsided");
2074 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2075 if ( flatsides && !strcmp(flatsides,"false") ) {
2076 ege_select_one_action_set_active( flat_action, 1 );
2077 gtk_action_set_sensitive( prop_action, TRUE );
2078 } else {
2079 ege_select_one_action_set_active( flat_action, 0 );
2080 gtk_action_set_sensitive( prop_action, FALSE );
2081 }
2082 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2083 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2084 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2085 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2086 if (r2 < r1) {
2087 gtk_adjustment_set_value(adj, r2/r1);
2088 } else {
2089 gtk_adjustment_set_value(adj, r1/r2);
2090 }
2091 } else if (!strcmp(name, "sodipodi:sides")) {
2092 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2093 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2094 }
2096 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2097 }
2100 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2101 {
2102 NULL, /* child_added */
2103 NULL, /* child_removed */
2104 star_tb_event_attr_changed,
2105 NULL, /* content_changed */
2106 NULL /* order_changed */
2107 };
2110 /**
2111 * \param selection Should not be NULL.
2112 */
2113 static void
2114 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2115 {
2116 int n_selected = 0;
2117 Inkscape::XML::Node *repr = NULL;
2119 purge_repr_listener( tbl, tbl );
2121 for (GSList const *items = selection->itemList();
2122 items != NULL;
2123 items = items->next)
2124 {
2125 if (SP_IS_STAR((SPItem *) items->data)) {
2126 n_selected++;
2127 repr = SP_OBJECT_REPR((SPItem *) items->data);
2128 }
2129 }
2131 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2133 if (n_selected == 0) {
2134 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2135 } else if (n_selected == 1) {
2136 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2138 if (repr) {
2139 g_object_set_data( tbl, "repr", repr );
2140 Inkscape::GC::anchor(repr);
2141 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2142 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2143 }
2144 } else {
2145 // FIXME: implement averaging of all parameters for multiple selected stars
2146 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2147 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2148 }
2149 }
2152 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2153 {
2154 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2155 // callbacks to lump all the changes for all selected objects in one undo step
2157 GtkAdjustment *adj = 0;
2159 // fixme: make settable in prefs!
2160 gint mag = 5;
2161 gdouble prop = 0.5;
2162 gboolean flat = FALSE;
2163 gdouble randomized = 0;
2164 gdouble rounded = 0;
2166 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2167 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2169 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2170 gtk_action_set_sensitive( sb2, !flat );
2172 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2173 gtk_adjustment_set_value(adj, mag);
2174 gtk_adjustment_value_changed(adj);
2176 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2177 gtk_adjustment_set_value(adj, prop);
2178 gtk_adjustment_value_changed(adj);
2180 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2181 gtk_adjustment_set_value(adj, rounded);
2182 gtk_adjustment_value_changed(adj);
2184 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2185 gtk_adjustment_set_value(adj, randomized);
2186 gtk_adjustment_value_changed(adj);
2187 }
2190 void
2191 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2192 {
2193 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2194 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2195 GtkWidget *l = gtk_label_new(NULL);
2196 gtk_label_set_markup(GTK_LABEL(l), title);
2197 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2198 if ( GTK_IS_TOOLBAR(tbl) ) {
2199 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2200 } else {
2201 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2202 }
2203 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2204 }
2207 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2208 {
2209 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2211 {
2212 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2213 ege_output_action_set_use_markup( act, TRUE );
2214 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2215 g_object_set_data( holder, "mode_action", act );
2216 }
2218 {
2219 EgeAdjustmentAction* eact = 0;
2220 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2221 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2223 /* Flatsided checkbox */
2224 {
2225 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2227 GtkTreeIter iter;
2228 gtk_list_store_append( model, &iter );
2229 gtk_list_store_set( model, &iter,
2230 0, _("Polygon"),
2231 1, _("Regular polygon (with one handle) instead of a star"),
2232 2, "star_flat",
2233 -1 );
2235 gtk_list_store_append( model, &iter );
2236 gtk_list_store_set( model, &iter,
2237 0, _("Star"),
2238 1, _("Star instead of a regular polygon (with one handle)"),
2239 2, "star_angled",
2240 -1 );
2242 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2243 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2244 g_object_set_data( holder, "flat_action", act );
2246 ege_select_one_action_set_appearance( act, "full" );
2247 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2248 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2249 ege_select_one_action_set_icon_column( act, 2 );
2250 ege_select_one_action_set_icon_size( act, secondarySize );
2251 ege_select_one_action_set_tooltip_column( act, 1 );
2253 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2254 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2255 }
2257 /* Magnitude */
2258 {
2259 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2260 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2261 eact = create_adjustment_action( "MagnitudeAction",
2262 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2263 "tools.shapes.star", "magnitude", 3,
2264 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2265 3, 1024, 1, 5,
2266 labels, values, G_N_ELEMENTS(labels),
2267 sp_stb_magnitude_value_changed,
2268 1.0, 0 );
2269 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2270 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2271 }
2273 /* Spoke ratio */
2274 {
2275 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2276 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2277 eact = create_adjustment_action( "SpokeAction",
2278 _("Spoke ratio"), _("Spoke ratio:"),
2279 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2280 // Base radius is the same for the closest handle.
2281 _("Base radius to tip radius ratio"),
2282 "tools.shapes.star", "proportion", 0.5,
2283 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2284 0.01, 1.0, 0.01, 0.1,
2285 labels, values, G_N_ELEMENTS(labels),
2286 sp_stb_proportion_value_changed );
2287 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2288 g_object_set_data( holder, "prop_action", eact );
2289 }
2291 if ( !isFlatSided ) {
2292 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2293 } else {
2294 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2295 }
2297 /* Roundedness */
2298 {
2299 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2300 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2301 eact = create_adjustment_action( "RoundednessAction",
2302 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2303 "tools.shapes.star", "rounded", 0.0,
2304 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2305 -10.0, 10.0, 0.01, 0.1,
2306 labels, values, G_N_ELEMENTS(labels),
2307 sp_stb_rounded_value_changed );
2308 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2309 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2310 }
2312 /* Randomization */
2313 {
2314 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2315 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2316 eact = create_adjustment_action( "RandomizationAction",
2317 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2318 "tools.shapes.star", "randomized", 0.0,
2319 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2320 -10.0, 10.0, 0.001, 0.01,
2321 labels, values, G_N_ELEMENTS(labels),
2322 sp_stb_randomized_value_changed, 0.1, 3 );
2323 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2324 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2325 }
2326 }
2328 {
2329 /* Reset */
2330 {
2331 GtkAction* act = gtk_action_new( "StarResetAction",
2332 _("Defaults"),
2333 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2334 GTK_STOCK_CLEAR );
2335 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2336 gtk_action_group_add_action( mainActions, act );
2337 gtk_action_set_sensitive( act, TRUE );
2338 }
2339 }
2341 sigc::connection *connection = new sigc::connection(
2342 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2343 );
2344 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2345 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2346 }
2349 //########################
2350 //## Rect ##
2351 //########################
2353 static void sp_rtb_sensitivize( GObject *tbl )
2354 {
2355 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2356 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2357 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2359 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2360 gtk_action_set_sensitive( not_rounded, FALSE );
2361 } else {
2362 gtk_action_set_sensitive( not_rounded, TRUE );
2363 }
2364 }
2367 static void
2368 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2369 void (*setter)(SPRect *, gdouble))
2370 {
2371 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2373 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2374 SPUnit const *unit = tracker->getActiveUnit();
2376 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2377 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2378 }
2380 // quit if run by the attr_changed listener
2381 if (g_object_get_data( tbl, "freeze" )) {
2382 return;
2383 }
2385 // in turn, prevent listener from responding
2386 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2388 bool modmade = false;
2389 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2390 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2391 if (SP_IS_RECT(items->data)) {
2392 if (adj->value != 0) {
2393 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2394 } else {
2395 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2396 }
2397 modmade = true;
2398 }
2399 }
2401 sp_rtb_sensitivize( tbl );
2403 if (modmade) {
2404 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2405 _("Change rectangle"));
2406 }
2408 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2409 }
2411 static void
2412 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2413 {
2414 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2415 }
2417 static void
2418 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2419 {
2420 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2421 }
2423 static void
2424 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2425 {
2426 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2427 }
2429 static void
2430 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2431 {
2432 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2433 }
2437 static void
2438 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2439 {
2440 GtkAdjustment *adj = 0;
2442 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2443 gtk_adjustment_set_value(adj, 0.0);
2444 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2445 gtk_adjustment_value_changed(adj);
2447 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2448 gtk_adjustment_set_value(adj, 0.0);
2449 gtk_adjustment_value_changed(adj);
2451 sp_rtb_sensitivize( obj );
2452 }
2454 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2455 gchar const */*old_value*/, gchar const */*new_value*/,
2456 bool /*is_interactive*/, gpointer data)
2457 {
2458 GObject *tbl = G_OBJECT(data);
2460 // quit if run by the _changed callbacks
2461 if (g_object_get_data( tbl, "freeze" )) {
2462 return;
2463 }
2465 // in turn, prevent callbacks from responding
2466 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2468 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2469 SPUnit const *unit = tracker->getActiveUnit();
2471 gpointer item = g_object_get_data( tbl, "item" );
2472 if (item && SP_IS_RECT(item)) {
2473 {
2474 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2475 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2476 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2477 }
2479 {
2480 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2481 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2482 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2483 }
2485 {
2486 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2487 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2488 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2489 }
2491 {
2492 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2493 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2494 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2495 }
2496 }
2498 sp_rtb_sensitivize( tbl );
2500 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2501 }
2504 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2505 NULL, /* child_added */
2506 NULL, /* child_removed */
2507 rect_tb_event_attr_changed,
2508 NULL, /* content_changed */
2509 NULL /* order_changed */
2510 };
2512 /**
2513 * \param selection should not be NULL.
2514 */
2515 static void
2516 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2517 {
2518 int n_selected = 0;
2519 Inkscape::XML::Node *repr = NULL;
2520 SPItem *item = NULL;
2522 if ( g_object_get_data( tbl, "repr" ) ) {
2523 g_object_set_data( tbl, "item", NULL );
2524 }
2525 purge_repr_listener( tbl, tbl );
2527 for (GSList const *items = selection->itemList();
2528 items != NULL;
2529 items = items->next) {
2530 if (SP_IS_RECT((SPItem *) items->data)) {
2531 n_selected++;
2532 item = (SPItem *) items->data;
2533 repr = SP_OBJECT_REPR(item);
2534 }
2535 }
2537 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2539 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2541 if (n_selected == 0) {
2542 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2544 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2545 gtk_action_set_sensitive(w, FALSE);
2546 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2547 gtk_action_set_sensitive(h, FALSE);
2549 } else if (n_selected == 1) {
2550 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2551 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2553 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2554 gtk_action_set_sensitive(w, TRUE);
2555 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2556 gtk_action_set_sensitive(h, TRUE);
2558 if (repr) {
2559 g_object_set_data( tbl, "repr", repr );
2560 g_object_set_data( tbl, "item", item );
2561 Inkscape::GC::anchor(repr);
2562 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2563 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2564 }
2565 } else {
2566 // FIXME: implement averaging of all parameters for multiple selected
2567 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2568 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2569 sp_rtb_sensitivize( tbl );
2570 }
2571 }
2574 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2575 {
2576 EgeAdjustmentAction* eact = 0;
2577 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2579 {
2580 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2581 ege_output_action_set_use_markup( act, TRUE );
2582 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2583 g_object_set_data( holder, "mode_action", act );
2584 }
2586 // rx/ry units menu: create
2587 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2588 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2589 // fixme: add % meaning per cent of the width/height
2590 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2591 g_object_set_data( holder, "tracker", tracker );
2593 /* W */
2594 {
2595 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2596 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2597 eact = create_adjustment_action( "RectWidthAction",
2598 _("Width"), _("W:"), _("Width of rectangle"),
2599 "tools.shapes.rect", "width", 0,
2600 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2601 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2602 labels, values, G_N_ELEMENTS(labels),
2603 sp_rtb_width_value_changed );
2604 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2605 g_object_set_data( holder, "width_action", eact );
2606 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2607 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2608 }
2610 /* H */
2611 {
2612 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2613 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2614 eact = create_adjustment_action( "RectHeightAction",
2615 _("Height"), _("H:"), _("Height of rectangle"),
2616 "tools.shapes.rect", "height", 0,
2617 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2618 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2619 labels, values, G_N_ELEMENTS(labels),
2620 sp_rtb_height_value_changed );
2621 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2622 g_object_set_data( holder, "height_action", eact );
2623 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2624 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2625 }
2627 /* rx */
2628 {
2629 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2630 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2631 eact = create_adjustment_action( "RadiusXAction",
2632 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2633 "tools.shapes.rect", "rx", 0,
2634 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2635 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2636 labels, values, G_N_ELEMENTS(labels),
2637 sp_rtb_rx_value_changed);
2638 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2639 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2640 }
2642 /* ry */
2643 {
2644 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2645 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2646 eact = create_adjustment_action( "RadiusYAction",
2647 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2648 "tools.shapes.rect", "ry", 0,
2649 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2650 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2651 labels, values, G_N_ELEMENTS(labels),
2652 sp_rtb_ry_value_changed);
2653 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2654 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2655 }
2657 // add the units menu
2658 {
2659 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2660 gtk_action_group_add_action( mainActions, act );
2661 }
2663 /* Reset */
2664 {
2665 InkAction* inky = ink_action_new( "RectResetAction",
2666 _("Not rounded"),
2667 _("Make corners sharp"),
2668 "squared_corner",
2669 secondarySize );
2670 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2671 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2672 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2673 g_object_set_data( holder, "not_rounded", inky );
2674 }
2676 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2677 sp_rtb_sensitivize( holder );
2679 sigc::connection *connection = new sigc::connection(
2680 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2681 );
2682 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2683 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2684 }
2686 //########################
2687 //## 3D Box ##
2688 //########################
2690 // normalize angle so that it lies in the interval [0,360]
2691 static double box3d_normalize_angle (double a) {
2692 double angle = a + ((int) (a/360.0))*360;
2693 if (angle < 0) {
2694 angle += 360.0;
2695 }
2696 return angle;
2697 }
2699 static void
2700 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2701 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2702 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2703 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2704 // are reset).
2705 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2707 if (is_infinite) {
2708 gtk_toggle_action_set_active(tact, TRUE);
2709 gtk_action_set_sensitive(act, TRUE);
2711 double angle = persp3d_get_infinite_angle(persp, axis);
2712 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2713 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2714 }
2715 } else {
2716 gtk_toggle_action_set_active(tact, FALSE);
2717 gtk_action_set_sensitive(act, FALSE);
2718 }
2719 }
2721 static void
2722 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2723 if (!persp_repr) {
2724 g_print ("No perspective given to box3d_resync_toolbar().\n");
2725 return;
2726 }
2728 GtkWidget *tbl = GTK_WIDGET(data);
2729 GtkAdjustment *adj = 0;
2730 GtkAction *act = 0;
2731 GtkToggleAction *tact = 0;
2732 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2733 {
2734 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2735 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2736 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2738 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2739 }
2740 {
2741 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2742 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2743 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2745 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2746 }
2747 {
2748 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2749 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2750 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2752 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2753 }
2754 }
2756 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2757 gchar const */*old_value*/, gchar const */*new_value*/,
2758 bool /*is_interactive*/, gpointer data)
2759 {
2760 GtkWidget *tbl = GTK_WIDGET(data);
2762 // quit if run by the attr_changed listener
2763 // note: it used to work without the differently called freeze_ attributes (here and in
2764 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2765 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2766 return;
2767 }
2769 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2770 // sp_document_maybe_done() when the document is undo insensitive)
2771 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2773 // TODO: Only update the appropriate part of the toolbar
2774 // if (!strcmp(name, "inkscape:vp_z")) {
2775 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2776 // }
2778 Persp3D *persp = persp3d_get_from_repr(repr);
2779 persp3d_update_box_reprs(persp);
2781 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2782 }
2784 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2785 {
2786 NULL, /* child_added */
2787 NULL, /* child_removed */
2788 box3d_persp_tb_event_attr_changed,
2789 NULL, /* content_changed */
2790 NULL /* order_changed */
2791 };
2793 /**
2794 * \param selection Should not be NULL.
2795 */
2796 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2797 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2798 static void
2799 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2800 {
2801 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2802 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2803 // update the perspectives with infinite VPs and leave the other ones untouched).
2805 Inkscape::XML::Node *persp_repr = NULL;
2806 purge_repr_listener(tbl, tbl);
2808 SPItem *item = selection->singleItem();
2809 if (item && SP_IS_BOX3D(item)) {
2810 // FIXME: Also deal with multiple selected boxes
2811 SPBox3D *box = SP_BOX3D(item);
2812 Persp3D *persp = box3d_get_perspective(box);
2813 persp_repr = SP_OBJECT_REPR(persp);
2814 if (persp_repr) {
2815 g_object_set_data(tbl, "repr", persp_repr);
2816 Inkscape::GC::anchor(persp_repr);
2817 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2818 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2819 }
2821 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2822 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2824 box3d_resync_toolbar(persp_repr, tbl);
2825 }
2826 }
2828 static void
2829 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2830 {
2831 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2832 SPDocument *document = sp_desktop_document(desktop);
2834 // quit if run by the attr_changed listener
2835 // note: it used to work without the differently called freeze_ attributes (here and in
2836 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2837 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2838 return;
2839 }
2841 // in turn, prevent listener from responding
2842 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2844 //Persp3D *persp = document->current_persp3d;
2845 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2846 if (sel_persps.empty()) {
2847 // this can happen when the document is created; we silently ignore it
2848 return;
2849 }
2850 Persp3D *persp = sel_persps.front();
2852 persp->tmat.set_infinite_direction (axis, adj->value);
2853 SP_OBJECT(persp)->updateRepr();
2855 // TODO: use the correct axis here, too
2856 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2858 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2859 }
2862 static void
2863 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2864 {
2865 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2866 }
2868 static void
2869 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2870 {
2871 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2872 }
2874 static void
2875 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2876 {
2877 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2878 }
2881 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2882 {
2883 // TODO: Take all selected perspectives into account
2884 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2885 if (sel_persps.empty()) {
2886 // this can happen when the document is created; we silently ignore it
2887 return;
2888 }
2889 Persp3D *persp = sel_persps.front();
2891 bool set_infinite = gtk_toggle_action_get_active(act);
2892 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2893 }
2895 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2896 {
2897 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2898 }
2900 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2901 {
2902 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2903 }
2905 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2906 {
2907 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2908 }
2910 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2911 {
2912 EgeAdjustmentAction* eact = 0;
2913 SPDocument *document = sp_desktop_document (desktop);
2914 Persp3D *persp = document->current_persp3d;
2916 EgeAdjustmentAction* box3d_angle_x = 0;
2917 EgeAdjustmentAction* box3d_angle_y = 0;
2918 EgeAdjustmentAction* box3d_angle_z = 0;
2920 /* Angle X */
2921 {
2922 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2923 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2924 eact = create_adjustment_action( "3DBoxAngleXAction",
2925 _("Angle in X direction"), _("Angle X:"),
2926 // Translators: PL is short for 'perspective line'
2927 _("Angle of PLs in X direction"),
2928 "tools.shapes.3dbox", "box3d_angle_x", 30,
2929 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2930 -360.0, 360.0, 1.0, 10.0,
2931 labels, values, G_N_ELEMENTS(labels),
2932 box3d_angle_x_value_changed );
2933 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2934 g_object_set_data( holder, "box3d_angle_x_action", eact );
2935 box3d_angle_x = eact;
2936 }
2938 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2939 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2940 } else {
2941 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2942 }
2945 /* VP X state */
2946 {
2947 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2948 // Translators: VP is short for 'vanishing point'
2949 _("State of VP in X direction"),
2950 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2951 "toggle_vp_x",
2952 Inkscape::ICON_SIZE_DECORATION );
2953 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2954 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2955 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2956 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2957 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2958 }
2960 /* Angle Y */
2961 {
2962 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2963 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2964 eact = create_adjustment_action( "3DBoxAngleYAction",
2965 _("Angle in Y direction"), _("Angle Y:"),
2966 // Translators: PL is short for 'perspective line'
2967 _("Angle of PLs in Y direction"),
2968 "tools.shapes.3dbox", "box3d_angle_y", 30,
2969 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2970 -360.0, 360.0, 1.0, 10.0,
2971 labels, values, G_N_ELEMENTS(labels),
2972 box3d_angle_y_value_changed );
2973 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2974 g_object_set_data( holder, "box3d_angle_y_action", eact );
2975 box3d_angle_y = eact;
2976 }
2978 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2979 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2980 } else {
2981 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2982 }
2984 /* VP Y state */
2985 {
2986 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2987 // Translators: VP is short for 'vanishing point'
2988 _("State of VP in Y direction"),
2989 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2990 "toggle_vp_y",
2991 Inkscape::ICON_SIZE_DECORATION );
2992 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2993 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2994 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2995 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2996 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2997 }
2999 /* Angle Z */
3000 {
3001 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3002 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3003 eact = create_adjustment_action( "3DBoxAngleZAction",
3004 _("Angle in Z direction"), _("Angle Z:"),
3005 // Translators: PL is short for 'perspective line'
3006 _("Angle of PLs in Z direction"),
3007 "tools.shapes.3dbox", "box3d_angle_z", 30,
3008 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3009 -360.0, 360.0, 1.0, 10.0,
3010 labels, values, G_N_ELEMENTS(labels),
3011 box3d_angle_z_value_changed );
3012 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3013 g_object_set_data( holder, "box3d_angle_z_action", eact );
3014 box3d_angle_z = eact;
3015 }
3017 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3018 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3019 } else {
3020 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3021 }
3023 /* VP Z state */
3024 {
3025 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3026 // Translators: VP is short for 'vanishing point'
3027 _("State of VP in Z direction"),
3028 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3029 "toggle_vp_z",
3030 Inkscape::ICON_SIZE_DECORATION );
3031 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3032 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3033 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3034 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3035 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3036 }
3038 sigc::connection *connection = new sigc::connection(
3039 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3040 );
3041 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3042 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3043 }
3045 //########################
3046 //## Spiral ##
3047 //########################
3049 static void
3050 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3051 {
3052 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3054 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3055 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3056 }
3058 // quit if run by the attr_changed listener
3059 if (g_object_get_data( tbl, "freeze" )) {
3060 return;
3061 }
3063 // in turn, prevent listener from responding
3064 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3066 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3068 bool modmade = false;
3069 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3070 items != NULL;
3071 items = items->next)
3072 {
3073 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3074 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3075 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3076 SP_OBJECT((SPItem *) items->data)->updateRepr();
3077 modmade = true;
3078 }
3079 }
3081 g_free(namespaced_name);
3083 if (modmade) {
3084 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3085 _("Change spiral"));
3086 }
3088 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3089 }
3091 static void
3092 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3093 {
3094 sp_spl_tb_value_changed(adj, tbl, "revolution");
3095 }
3097 static void
3098 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3099 {
3100 sp_spl_tb_value_changed(adj, tbl, "expansion");
3101 }
3103 static void
3104 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3105 {
3106 sp_spl_tb_value_changed(adj, tbl, "t0");
3107 }
3109 static void
3110 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3111 {
3112 GtkWidget *tbl = GTK_WIDGET(obj);
3114 GtkAdjustment *adj;
3116 // fixme: make settable
3117 gdouble rev = 5;
3118 gdouble exp = 1.0;
3119 gdouble t0 = 0.0;
3121 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3122 gtk_adjustment_set_value(adj, rev);
3123 gtk_adjustment_value_changed(adj);
3125 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3126 gtk_adjustment_set_value(adj, exp);
3127 gtk_adjustment_value_changed(adj);
3129 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3130 gtk_adjustment_set_value(adj, t0);
3131 gtk_adjustment_value_changed(adj);
3133 spinbutton_defocus(GTK_OBJECT(tbl));
3134 }
3137 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3138 gchar const */*old_value*/, gchar const */*new_value*/,
3139 bool /*is_interactive*/, gpointer data)
3140 {
3141 GtkWidget *tbl = GTK_WIDGET(data);
3143 // quit if run by the _changed callbacks
3144 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3145 return;
3146 }
3148 // in turn, prevent callbacks from responding
3149 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3151 GtkAdjustment *adj;
3152 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3153 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3155 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3156 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3158 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3159 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3161 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3162 }
3165 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3166 NULL, /* child_added */
3167 NULL, /* child_removed */
3168 spiral_tb_event_attr_changed,
3169 NULL, /* content_changed */
3170 NULL /* order_changed */
3171 };
3173 static void
3174 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3175 {
3176 int n_selected = 0;
3177 Inkscape::XML::Node *repr = NULL;
3179 purge_repr_listener( tbl, tbl );
3181 for (GSList const *items = selection->itemList();
3182 items != NULL;
3183 items = items->next)
3184 {
3185 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3186 n_selected++;
3187 repr = SP_OBJECT_REPR((SPItem *) items->data);
3188 }
3189 }
3191 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3193 if (n_selected == 0) {
3194 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3195 } else if (n_selected == 1) {
3196 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3198 if (repr) {
3199 g_object_set_data( tbl, "repr", repr );
3200 Inkscape::GC::anchor(repr);
3201 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3202 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3203 }
3204 } else {
3205 // FIXME: implement averaging of all parameters for multiple selected
3206 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3207 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3208 }
3209 }
3212 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3213 {
3214 EgeAdjustmentAction* eact = 0;
3215 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3217 {
3218 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3219 ege_output_action_set_use_markup( act, TRUE );
3220 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3221 g_object_set_data( holder, "mode_action", act );
3222 }
3224 /* Revolution */
3225 {
3226 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3227 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3228 eact = create_adjustment_action( "SpiralRevolutionAction",
3229 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3230 "tools.shapes.spiral", "revolution", 3.0,
3231 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3232 0.01, 1024.0, 0.1, 1.0,
3233 labels, values, G_N_ELEMENTS(labels),
3234 sp_spl_tb_revolution_value_changed, 1, 2);
3235 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3236 }
3238 /* Expansion */
3239 {
3240 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3241 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3242 eact = create_adjustment_action( "SpiralExpansionAction",
3243 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3244 "tools.shapes.spiral", "expansion", 1.0,
3245 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3246 0.0, 1000.0, 0.01, 1.0,
3247 labels, values, G_N_ELEMENTS(labels),
3248 sp_spl_tb_expansion_value_changed);
3249 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3250 }
3252 /* T0 */
3253 {
3254 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3255 gdouble values[] = {0, 0.5, 0.9};
3256 eact = create_adjustment_action( "SpiralT0Action",
3257 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3258 "tools.shapes.spiral", "t0", 0.0,
3259 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3260 0.0, 0.999, 0.01, 1.0,
3261 labels, values, G_N_ELEMENTS(labels),
3262 sp_spl_tb_t0_value_changed);
3263 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3264 }
3266 /* Reset */
3267 {
3268 InkAction* inky = ink_action_new( "SpiralResetAction",
3269 _("Defaults"),
3270 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3271 GTK_STOCK_CLEAR,
3272 secondarySize );
3273 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3274 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3275 }
3278 sigc::connection *connection = new sigc::connection(
3279 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3280 );
3281 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3282 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3283 }
3285 //########################
3286 //## Pen/Pencil ##
3287 //########################
3289 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3290 static char const *
3291 freehand_tool_name(GObject *dataKludge)
3292 {
3293 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3294 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3295 ? "tools.freehand.pen"
3296 : "tools.freehand.pencil" );
3297 }
3299 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3300 {
3301 gint mode = ege_select_one_action_get_active(act);
3303 prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3305 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3307 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3308 // preparatory work here
3309 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3310 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3311 sp_pen_context_set_polyline_mode(pc);
3312 }
3313 }
3315 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3316 {
3317 /* Freehand mode toggle buttons */
3318 {
3319 guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3320 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3322 {
3323 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3325 GtkTreeIter iter;
3326 gtk_list_store_append( model, &iter );
3327 gtk_list_store_set( model, &iter,
3328 0, _("Bezier"),
3329 1, _("Create regular Bezier path"),
3330 2, "bezier_mode",
3331 -1 );
3333 gtk_list_store_append( model, &iter );
3334 gtk_list_store_set( model, &iter,
3335 0, _("Spiro"),
3336 1, _("Create Spiro path"),
3337 2, "spiro_splines_mode",
3338 -1 );
3340 if (!tool_is_pencil) {
3341 gtk_list_store_append( model, &iter );
3342 gtk_list_store_set( model, &iter,
3343 0, _("Zigzag"),
3344 1, _("Create a sequence of straight line segments"),
3345 2, "polylines_mode",
3346 -1 );
3348 gtk_list_store_append( model, &iter );
3349 gtk_list_store_set( model, &iter,
3350 0, _("Paraxial"),
3351 1, _("Create a sequence of paraxial line segments"),
3352 2, "paraxial_lines_mode",
3353 -1 );
3354 }
3356 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3357 "FreehandModeActionPencil" :
3358 "FreehandModeActionPen",
3359 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3360 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3362 ege_select_one_action_set_appearance( act, "full" );
3363 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3364 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3365 ege_select_one_action_set_icon_column( act, 2 );
3366 ege_select_one_action_set_icon_size( act, secondarySize );
3367 ege_select_one_action_set_tooltip_column( act, 1 );
3369 ege_select_one_action_set_active( act, freehandMode);
3370 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3371 }
3372 }
3373 }
3375 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3376 gint shape = ege_select_one_action_get_active( act );
3377 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3378 }
3380 /**
3381 * \brief Generate the list of freehand advanced shape option entries.
3382 */
3383 GList * freehand_shape_dropdown_items_list() {
3384 GList *glist = NULL;
3386 glist = g_list_append (glist, _("None"));
3387 glist = g_list_append (glist, _("Triangle in"));
3388 glist = g_list_append (glist, _("Triangle out"));
3389 glist = g_list_append (glist, _("Ellipse"));
3390 glist = g_list_append (glist, _("From clipboard"));
3392 return glist;
3393 }
3395 static void
3396 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3397 /*advanced shape options */
3398 {
3399 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3401 GList* items = 0;
3402 gint count = 0;
3403 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3404 {
3405 GtkTreeIter iter;
3406 gtk_list_store_append( model, &iter );
3407 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3408 count++;
3409 }
3410 g_list_free( items );
3411 items = 0;
3412 EgeSelectOneAction* act1 = ege_select_one_action_new(
3413 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3414 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3415 g_object_set( act1, "short_label", _("Shape:"), NULL );
3416 ege_select_one_action_set_appearance( act1, "compact" );
3417 ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3418 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3419 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3420 g_object_set_data( holder, "shape_action", act1 );
3421 }
3422 }
3424 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3425 {
3426 sp_add_freehand_mode_toggle(mainActions, holder, false);
3427 freehand_add_advanced_shape_options(mainActions, holder, false);
3428 }
3431 static void
3432 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3433 {
3434 GtkWidget *tbl = GTK_WIDGET(obj);
3436 GtkAdjustment *adj;
3438 // fixme: make settable
3439 gdouble tolerance = 4;
3441 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3442 gtk_adjustment_set_value(adj, tolerance);
3443 gtk_adjustment_value_changed(adj);
3445 spinbutton_defocus(GTK_OBJECT(tbl));
3446 }
3448 static void
3449 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3450 {
3452 // quit if run by the attr_changed listener
3453 if (g_object_get_data( tbl, "freeze" )) {
3454 return;
3455 }
3456 // in turn, prevent listener from responding
3457 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3458 prefs_set_double_attribute("tools.freehand.pencil",
3459 "tolerance", adj->value);
3460 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3462 }
3466 static void
3467 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3468 const gchar */*key*/,
3469 const gchar */*oldval*/,
3470 const gchar */*newval*/,
3471 bool /*is_interactive*/,
3472 void * data)
3473 {
3474 GObject* tbl = G_OBJECT(data);
3475 if (g_object_get_data( tbl, "freeze" )) {
3476 return;
3477 }
3479 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3481 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3482 "tolerance");
3484 double v = prefs_get_double_attribute("tools.freehand.pencil",
3485 "tolerance", adj->value);
3486 gtk_adjustment_set_value(adj, v);
3487 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3489 }
3491 static Inkscape::XML::NodeEventVector pencil_node_events =
3492 {
3493 NULL,
3494 NULL,
3495 sp_pencil_tb_tolerance_value_changed_external,
3496 NULL,
3497 NULL,
3498 };
3501 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3502 {
3503 sp_add_freehand_mode_toggle(mainActions, holder, true);
3505 EgeAdjustmentAction* eact = 0;
3507 /* Tolerance */
3508 {
3509 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3510 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3511 eact = create_adjustment_action( "PencilToleranceAction",
3512 _("Smoothing:"), _("Smoothing: "),
3513 _("How much smoothing (simplifying) is applied to the line"),
3514 "tools.freehand.pencil", "tolerance",
3515 3.0,
3516 GTK_WIDGET(desktop->canvas), NULL,
3517 holder, TRUE, "altx-pencil",
3518 1, 100.0, 0.5, 0,
3519 labels, values, G_N_ELEMENTS(labels),
3520 sp_pencil_tb_tolerance_value_changed,
3521 1, 2);
3522 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3523 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3525 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3526 "tools.freehand.pencil");
3527 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3528 g_object_set_data(G_OBJECT(holder), "repr", repr);
3530 }
3532 /* advanced shape options */
3533 freehand_add_advanced_shape_options(mainActions, holder, true);
3535 /* Reset */
3536 {
3537 InkAction* inky = ink_action_new( "PencilResetAction",
3538 _("Defaults"),
3539 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3540 GTK_STOCK_CLEAR,
3541 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3542 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3543 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3544 }
3546 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3548 }
3551 //########################
3552 //## Tweak ##
3553 //########################
3555 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3556 {
3557 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3558 }
3560 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3561 {
3562 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3563 }
3565 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3566 {
3567 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3568 }
3570 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3571 {
3572 int mode = ege_select_one_action_get_active( act );
3573 prefs_set_int_attribute("tools.tweak", "mode", mode);
3575 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3576 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3577 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3578 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3579 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3580 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3581 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3582 if (doh) gtk_action_set_sensitive (doh, TRUE);
3583 if (dos) gtk_action_set_sensitive (dos, TRUE);
3584 if (dol) gtk_action_set_sensitive (dol, TRUE);
3585 if (doo) gtk_action_set_sensitive (doo, TRUE);
3586 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3587 if (fid) gtk_action_set_sensitive (fid, FALSE);
3588 } else {
3589 if (doh) gtk_action_set_sensitive (doh, FALSE);
3590 if (dos) gtk_action_set_sensitive (dos, FALSE);
3591 if (dol) gtk_action_set_sensitive (dol, FALSE);
3592 if (doo) gtk_action_set_sensitive (doo, FALSE);
3593 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3594 if (fid) gtk_action_set_sensitive (fid, TRUE);
3595 }
3596 }
3598 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3599 {
3600 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3601 }
3603 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3604 bool show = gtk_toggle_action_get_active( act );
3605 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3606 }
3607 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3608 bool show = gtk_toggle_action_get_active( act );
3609 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3610 }
3611 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3612 bool show = gtk_toggle_action_get_active( act );
3613 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3614 }
3615 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3616 bool show = gtk_toggle_action_get_active( act );
3617 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3618 }
3620 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3621 {
3622 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3624 {
3625 /* Width */
3626 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3627 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3628 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3629 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3630 "tools.tweak", "width", 15,
3631 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3632 1, 100, 1.0, 0.0,
3633 labels, values, G_N_ELEMENTS(labels),
3634 sp_tweak_width_value_changed, 0.01, 0, 100 );
3635 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3636 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3637 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3638 }
3641 {
3642 /* Force */
3643 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3644 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3645 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3646 _("Force"), _("Force:"), _("The force of the tweak action"),
3647 "tools.tweak", "force", 20,
3648 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3649 1, 100, 1.0, 0.0,
3650 labels, values, G_N_ELEMENTS(labels),
3651 sp_tweak_force_value_changed, 0.01, 0, 100 );
3652 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3653 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3654 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3655 }
3657 /* Mode */
3658 {
3659 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3661 GtkTreeIter iter;
3662 gtk_list_store_append( model, &iter );
3663 gtk_list_store_set( model, &iter,
3664 0, _("Push mode"),
3665 1, _("Push parts of paths in any direction"),
3666 2, "tweak_push_mode",
3667 -1 );
3669 gtk_list_store_append( model, &iter );
3670 gtk_list_store_set( model, &iter,
3671 0, _("Shrink mode"),
3672 1, _("Shrink (inset) parts of paths"),
3673 2, "tweak_shrink_mode",
3674 -1 );
3676 gtk_list_store_append( model, &iter );
3677 gtk_list_store_set( model, &iter,
3678 0, _("Grow mode"),
3679 1, _("Grow (outset) parts of paths"),
3680 2, "tweak_grow_mode",
3681 -1 );
3683 gtk_list_store_append( model, &iter );
3684 gtk_list_store_set( model, &iter,
3685 0, _("Attract mode"),
3686 1, _("Attract parts of paths towards cursor"),
3687 2, "tweak_attract_mode",
3688 -1 );
3690 gtk_list_store_append( model, &iter );
3691 gtk_list_store_set( model, &iter,
3692 0, _("Repel mode"),
3693 1, _("Repel parts of paths from cursor"),
3694 2, "tweak_repel_mode",
3695 -1 );
3697 gtk_list_store_append( model, &iter );
3698 gtk_list_store_set( model, &iter,
3699 0, _("Roughen mode"),
3700 1, _("Roughen parts of paths"),
3701 2, "tweak_roughen_mode",
3702 -1 );
3704 gtk_list_store_append( model, &iter );
3705 gtk_list_store_set( model, &iter,
3706 0, _("Color paint mode"),
3707 1, _("Paint the tool's color upon selected objects"),
3708 2, "tweak_colorpaint_mode",
3709 -1 );
3711 gtk_list_store_append( model, &iter );
3712 gtk_list_store_set( model, &iter,
3713 0, _("Color jitter mode"),
3714 1, _("Jitter the colors of selected objects"),
3715 2, "tweak_colorjitter_mode",
3716 -1 );
3718 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3719 g_object_set( act, "short_label", _("Mode:"), NULL );
3720 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3721 g_object_set_data( holder, "mode_action", act );
3723 ege_select_one_action_set_appearance( act, "full" );
3724 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3725 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3726 ege_select_one_action_set_icon_column( act, 2 );
3727 ege_select_one_action_set_icon_size( act, secondarySize );
3728 ege_select_one_action_set_tooltip_column( act, 1 );
3730 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3731 ege_select_one_action_set_active( act, mode );
3732 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3734 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3735 }
3737 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3739 {
3740 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3741 ege_output_action_set_use_markup( act, TRUE );
3742 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3743 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3744 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3745 g_object_set_data( holder, "tweak_channels_label", act);
3746 }
3748 {
3749 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3750 _("Hue"),
3751 _("In color mode, act on objects' hue"),
3752 NULL,
3753 Inkscape::ICON_SIZE_DECORATION );
3754 //TRANSLATORS: "H" here stands for hue
3755 g_object_set( act, "short_label", _("H"), NULL );
3756 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3757 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3758 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3759 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3760 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3761 g_object_set_data( holder, "tweak_doh", act);
3762 }
3763 {
3764 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3765 _("Saturation"),
3766 _("In color mode, act on objects' saturation"),
3767 NULL,
3768 Inkscape::ICON_SIZE_DECORATION );
3769 //TRANSLATORS: "S" here stands for Saturation
3770 g_object_set( act, "short_label", _("S"), NULL );
3771 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3772 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3773 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3774 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3775 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3776 g_object_set_data( holder, "tweak_dos", act );
3777 }
3778 {
3779 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3780 _("Lightness"),
3781 _("In color mode, act on objects' lightness"),
3782 NULL,
3783 Inkscape::ICON_SIZE_DECORATION );
3784 //TRANSLATORS: "L" here stands for Lightness
3785 g_object_set( act, "short_label", _("L"), NULL );
3786 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3787 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3788 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3789 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3790 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3791 g_object_set_data( holder, "tweak_dol", act );
3792 }
3793 {
3794 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3795 _("Opacity"),
3796 _("In color mode, act on objects' opacity"),
3797 NULL,
3798 Inkscape::ICON_SIZE_DECORATION );
3799 //TRANSLATORS: "O" here stands for Opacity
3800 g_object_set( act, "short_label", _("O"), NULL );
3801 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3802 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3803 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3804 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3805 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3806 g_object_set_data( holder, "tweak_doo", act );
3807 }
3809 { /* Fidelity */
3810 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3811 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3812 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3813 _("Fidelity"), _("Fidelity:"),
3814 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3815 "tools.tweak", "fidelity", 50,
3816 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3817 1, 100, 1.0, 10.0,
3818 labels, values, G_N_ELEMENTS(labels),
3819 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3820 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3821 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3822 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3823 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3824 g_object_set_data( holder, "tweak_fidelity", eact );
3825 }
3828 /* Use Pressure button */
3829 {
3830 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3831 _("Pressure"),
3832 _("Use the pressure of the input device to alter the force of tweak action"),
3833 "use_pressure",
3834 Inkscape::ICON_SIZE_DECORATION );
3835 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3836 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3837 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3838 }
3840 }
3843 //########################
3844 //## Calligraphy ##
3845 //########################
3846 static void update_presets_list (GObject *tbl)
3847 {
3848 if (g_object_get_data(tbl, "presets_blocked"))
3849 return;
3851 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3852 if (!sel) {
3853 ege_select_one_action_set_active(sel, 0);
3854 return;
3855 }
3857 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3859 for (int i = 1; i <= total_prefs; i++) {
3860 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3861 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3863 bool match = true;
3865 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3866 iter;
3867 ++iter ) {
3868 const gchar *attr_name = g_quark_to_string(iter->key);
3869 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3870 continue;
3871 void *widget = g_object_get_data(tbl, attr_name);
3872 if (widget) {
3873 if (GTK_IS_ADJUSTMENT(widget)) {
3874 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3875 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3876 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3877 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3878 match = false;
3879 break;
3880 }
3881 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3882 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3883 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3884 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3885 if (gtk_toggle_action_get_active(toggle) != v) {
3886 match = false;
3887 break;
3888 }
3889 }
3890 }
3891 }
3893 if (match) {
3894 // newly added item is at the same index as the
3895 // save command, so we need to change twice for it to take effect
3896 ege_select_one_action_set_active(sel, 0);
3897 ege_select_one_action_set_active(sel, i);
3898 return;
3899 }
3900 }
3902 // no match found
3903 ege_select_one_action_set_active(sel, 0);
3904 }
3906 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3907 {
3908 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3909 update_presets_list(tbl);
3910 }
3912 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3913 {
3914 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3915 update_presets_list(tbl);
3916 }
3918 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3919 {
3920 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3921 update_presets_list(tbl);
3922 }
3924 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3925 {
3926 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3927 update_presets_list(tbl);
3928 }
3930 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3931 {
3932 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3933 update_presets_list(tbl);
3934 }
3936 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3937 {
3938 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3939 update_presets_list(tbl);
3940 }
3942 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3943 {
3944 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3945 update_presets_list(tbl);
3946 }
3948 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3949 {
3950 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3951 update_presets_list(tbl);
3952 }
3954 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3955 {
3956 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3957 update_presets_list(tbl);
3958 }
3960 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3961 {
3962 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3963 update_presets_list(tbl);
3964 }
3966 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3967 {
3968 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3969 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3970 update_presets_list(tbl);
3971 if (calligraphy_angle )
3972 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3973 }
3976 static gchar const *const widget_names[] = {
3977 "width",
3978 "mass",
3979 "wiggle",
3980 "angle",
3981 "thinning",
3982 "tremor",
3983 "flatness",
3984 "cap_rounding",
3985 "usepressure",
3986 "tracebackground",
3987 "usetilt"
3988 };
3991 static void sp_dcc_build_presets_list(GObject *tbl)
3992 {
3993 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3995 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3996 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3997 gtk_list_store_clear (model);
3999 {
4000 GtkTreeIter iter;
4001 gtk_list_store_append( model, &iter );
4002 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4003 }
4005 //TODO: switch back to prefs API
4006 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4007 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4008 int ii=1;
4009 while (child_repr) {
4010 GtkTreeIter iter;
4011 char *preset_name = (char *) child_repr->attribute("name");
4012 gtk_list_store_append( model, &iter );
4013 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4014 child_repr = sp_repr_next(child_repr);
4015 }
4017 {
4018 GtkTreeIter iter;
4019 gtk_list_store_append( model, &iter );
4020 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4021 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4022 }
4024 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4026 update_presets_list (tbl);
4027 }
4029 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4030 {
4031 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4032 if (! desktop) return;
4034 if (g_object_get_data(tbl, "presets_blocked"))
4035 return;
4037 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4038 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4039 // dialog cancelled
4040 update_presets_list (tbl);
4041 return;
4042 }
4043 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4045 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4046 // empty name entered
4047 update_presets_list (tbl);
4048 return;
4049 }
4051 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4053 int new_index = -1;
4054 gchar *pref_path = NULL;
4055 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4057 for (int i = 1; i <= total_prefs; i++) {
4058 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4059 const gchar *name = prefs_get_string_attribute(path, "name");
4060 if (name && !strcmp(name, profile_name.c_str())) {
4061 // we already have preset with this name, replace its values
4062 new_index = i;
4063 pref_path = g_strdup(path);
4064 break;
4065 }
4066 }
4068 if (new_index == -1) {
4069 // no preset with this name, create
4070 new_index = total_prefs + 1;
4071 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4072 pref_path = create_pref("tools.calligraphic.preset", profile_id);
4073 g_free(profile_id);
4074 }
4076 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4077 gchar const *const widget_name = widget_names[i];
4078 void *widget = g_object_get_data(tbl, widget_name);
4079 if (widget) {
4080 if (GTK_IS_ADJUSTMENT(widget)) {
4081 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4082 double v = gtk_adjustment_get_value(adj);
4083 prefs_set_double_attribute(pref_path, widget_name, v);
4084 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4085 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4086 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4087 int v = gtk_toggle_action_get_active(toggle);
4088 prefs_set_int_attribute(pref_path, widget_name, v);
4089 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4090 } else {
4091 g_warning("Unknown widget type for preset: %s\n", widget_name);
4092 }
4093 } else {
4094 g_warning("Bad key when writing preset: %s\n", widget_name);
4095 }
4096 }
4097 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4099 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4101 sp_dcc_build_presets_list (tbl);
4103 g_free(pref_path);
4104 }
4107 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4109 gint preset_index = ege_select_one_action_get_active( act );
4110 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4112 if (preset_index == save_presets_index) {
4113 // this is the Save command
4114 sp_dcc_save_profile(NULL, tbl);
4115 return;
4116 }
4118 if (g_object_get_data(tbl, "presets_blocked"))
4119 return;
4121 gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4123 if (preset_path) {
4124 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
4126 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4128 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4129 iter;
4130 ++iter ) {
4131 const gchar *attr_name = g_quark_to_string(iter->key);
4132 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4133 continue;
4134 void *widget = g_object_get_data(tbl, attr_name);
4135 if (widget) {
4136 if (GTK_IS_ADJUSTMENT(widget)) {
4137 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4138 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4139 gtk_adjustment_set_value(adj, v);
4140 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4141 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4142 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4143 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4144 gtk_toggle_action_set_active(toggle, v);
4145 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4146 } else {
4147 g_warning("Unknown widget type for preset: %s\n", attr_name);
4148 }
4149 } else {
4150 g_warning("Bad key found in a preset record: %s\n", attr_name);
4151 }
4152 }
4153 g_free(preset_path);
4154 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4155 }
4157 }
4160 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4161 {
4162 {
4163 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4165 EgeAdjustmentAction* calligraphy_angle = 0;
4167 {
4168 /* Width */
4169 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4170 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4171 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4172 _("Pen Width"), _("Width:"),
4173 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4174 "tools.calligraphic", "width", 15,
4175 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4176 1, 100, 1.0, 0.0,
4177 labels, values, G_N_ELEMENTS(labels),
4178 sp_ddc_width_value_changed, 0.01, 0, 100 );
4179 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4180 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4181 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4182 }
4184 {
4185 /* Thinning */
4186 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4187 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4188 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4189 _("Stroke Thinning"), _("Thinning:"),
4190 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4191 "tools.calligraphic", "thinning", 10,
4192 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4193 -100, 100, 1, 0.1,
4194 labels, values, G_N_ELEMENTS(labels),
4195 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4196 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4197 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4198 }
4200 {
4201 /* Angle */
4202 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4203 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4204 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4205 _("Pen Angle"), _("Angle:"),
4206 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4207 "tools.calligraphic", "angle", 30,
4208 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4209 -90.0, 90.0, 1.0, 10.0,
4210 labels, values, G_N_ELEMENTS(labels),
4211 sp_ddc_angle_value_changed, 1, 0 );
4212 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4213 g_object_set_data( holder, "angle_action", eact );
4214 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4215 calligraphy_angle = eact;
4216 }
4218 {
4219 /* Fixation */
4220 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4221 gdouble values[] = {0, 20, 40, 60, 90, 100};
4222 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4223 _("Fixation"), _("Fixation:"),
4224 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4225 "tools.calligraphic", "flatness", 90,
4226 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4227 0.0, 100, 1.0, 10.0,
4228 labels, values, G_N_ELEMENTS(labels),
4229 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4230 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4231 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4232 }
4234 {
4235 /* Cap Rounding */
4236 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4237 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4238 // TRANSLATORS: "cap" means "end" (both start and finish) here
4239 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4240 _("Cap rounding"), _("Caps:"),
4241 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4242 "tools.calligraphic", "cap_rounding", 0.0,
4243 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4244 0.0, 5.0, 0.01, 0.1,
4245 labels, values, G_N_ELEMENTS(labels),
4246 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4247 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4248 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4249 }
4251 {
4252 /* Tremor */
4253 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4254 gdouble values[] = {0, 10, 20, 40, 60, 100};
4255 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4256 _("Stroke Tremor"), _("Tremor:"),
4257 _("Increase to make strokes rugged and trembling"),
4258 "tools.calligraphic", "tremor", 0.0,
4259 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4260 0.0, 100, 1, 0.0,
4261 labels, values, G_N_ELEMENTS(labels),
4262 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4264 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4265 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4266 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4267 }
4269 {
4270 /* Wiggle */
4271 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4272 gdouble values[] = {0, 20, 40, 60, 100};
4273 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4274 _("Pen Wiggle"), _("Wiggle:"),
4275 _("Increase to make the pen waver and wiggle"),
4276 "tools.calligraphic", "wiggle", 0.0,
4277 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4278 0.0, 100, 1, 0.0,
4279 labels, values, G_N_ELEMENTS(labels),
4280 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4281 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4282 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4283 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4284 }
4286 {
4287 /* Mass */
4288 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4289 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4290 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4291 _("Pen Mass"), _("Mass:"),
4292 _("Increase to make the pen drag behind, as if slowed by inertia"),
4293 "tools.calligraphic", "mass", 2.0,
4294 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4295 0.0, 100, 1, 0.0,
4296 labels, values, G_N_ELEMENTS(labels),
4297 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4298 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4299 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4300 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4301 }
4304 /* Trace Background button */
4305 {
4306 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4307 _("Trace Background"),
4308 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4309 "trace_background",
4310 Inkscape::ICON_SIZE_DECORATION );
4311 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4312 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4313 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4314 g_object_set_data( holder, "tracebackground", act );
4315 }
4317 /* Use Pressure button */
4318 {
4319 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4320 _("Pressure"),
4321 _("Use the pressure of the input device to alter the width of the pen"),
4322 "use_pressure",
4323 Inkscape::ICON_SIZE_DECORATION );
4324 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4325 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4326 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4327 g_object_set_data( holder, "usepressure", act );
4328 }
4330 /* Use Tilt button */
4331 {
4332 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4333 _("Tilt"),
4334 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4335 "use_tilt",
4336 Inkscape::ICON_SIZE_DECORATION );
4337 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4338 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4339 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4340 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4341 g_object_set_data( holder, "usetilt", act );
4342 }
4344 /*calligraphic profile */
4345 {
4346 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4347 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4348 ege_select_one_action_set_appearance (act1, "compact");
4349 g_object_set_data (holder, "profile_selector", act1 );
4351 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4353 sp_dcc_build_presets_list (holder);
4355 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4356 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4357 }
4358 }
4359 }
4362 //########################
4363 //## Circle / Arc ##
4364 //########################
4366 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4367 {
4368 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4369 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4371 if (v1 == 0 && v2 == 0) {
4372 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4373 gtk_action_set_sensitive( ocb, FALSE );
4374 gtk_action_set_sensitive( make_whole, FALSE );
4375 }
4376 } else {
4377 gtk_action_set_sensitive( ocb, TRUE );
4378 gtk_action_set_sensitive( make_whole, TRUE );
4379 }
4380 }
4382 static void
4383 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4384 {
4385 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4387 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4388 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4389 }
4391 // quit if run by the attr_changed listener
4392 if (g_object_get_data( tbl, "freeze" )) {
4393 return;
4394 }
4396 // in turn, prevent listener from responding
4397 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4399 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4401 bool modmade = false;
4402 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4403 items != NULL;
4404 items = items->next)
4405 {
4406 SPItem *item = SP_ITEM(items->data);
4408 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4410 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4411 SPArc *arc = SP_ARC(item);
4413 if (!strcmp(value_name, "start"))
4414 ge->start = (adj->value * M_PI)/ 180;
4415 else
4416 ge->end = (adj->value * M_PI)/ 180;
4418 sp_genericellipse_normalize(ge);
4419 ((SPObject *)arc)->updateRepr();
4420 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4422 modmade = true;
4423 }
4424 }
4426 g_free(namespaced_name);
4428 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4430 sp_arctb_sensitivize( tbl, adj->value, other->value );
4432 if (modmade) {
4433 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4434 _("Arc: Change start/end"));
4435 }
4437 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4438 }
4441 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4442 {
4443 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4444 }
4446 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4447 {
4448 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4449 }
4452 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4453 {
4454 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4455 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4456 if ( ege_select_one_action_get_active( act ) != 0 ) {
4457 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4458 } else {
4459 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4460 }
4461 }
4463 // quit if run by the attr_changed listener
4464 if (g_object_get_data( tbl, "freeze" )) {
4465 return;
4466 }
4468 // in turn, prevent listener from responding
4469 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4471 bool modmade = false;
4473 if ( ege_select_one_action_get_active(act) != 0 ) {
4474 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4475 items != NULL;
4476 items = items->next)
4477 {
4478 if (SP_IS_ARC((SPItem *) items->data)) {
4479 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4480 repr->setAttribute("sodipodi:open", "true");
4481 SP_OBJECT((SPItem *) items->data)->updateRepr();
4482 modmade = true;
4483 }
4484 }
4485 } else {
4486 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4487 items != NULL;
4488 items = items->next)
4489 {
4490 if (SP_IS_ARC((SPItem *) items->data)) {
4491 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4492 repr->setAttribute("sodipodi:open", NULL);
4493 SP_OBJECT((SPItem *) items->data)->updateRepr();
4494 modmade = true;
4495 }
4496 }
4497 }
4499 if (modmade) {
4500 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4501 _("Arc: Change open/closed"));
4502 }
4504 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4505 }
4507 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4508 {
4509 GtkAdjustment *adj;
4510 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4511 gtk_adjustment_set_value(adj, 0.0);
4512 gtk_adjustment_value_changed(adj);
4514 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4515 gtk_adjustment_set_value(adj, 0.0);
4516 gtk_adjustment_value_changed(adj);
4518 spinbutton_defocus( GTK_OBJECT(obj) );
4519 }
4521 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4522 gchar const */*old_value*/, gchar const */*new_value*/,
4523 bool /*is_interactive*/, gpointer data)
4524 {
4525 GObject *tbl = G_OBJECT(data);
4527 // quit if run by the _changed callbacks
4528 if (g_object_get_data( tbl, "freeze" )) {
4529 return;
4530 }
4532 // in turn, prevent callbacks from responding
4533 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4535 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4536 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4538 GtkAdjustment *adj1,*adj2;
4539 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4540 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4541 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4542 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4544 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4546 char const *openstr = NULL;
4547 openstr = repr->attribute("sodipodi:open");
4548 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4550 if (openstr) {
4551 ege_select_one_action_set_active( ocb, 1 );
4552 } else {
4553 ege_select_one_action_set_active( ocb, 0 );
4554 }
4556 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4557 }
4559 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4560 NULL, /* child_added */
4561 NULL, /* child_removed */
4562 arc_tb_event_attr_changed,
4563 NULL, /* content_changed */
4564 NULL /* order_changed */
4565 };
4568 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4569 {
4570 int n_selected = 0;
4571 Inkscape::XML::Node *repr = NULL;
4573 purge_repr_listener( tbl, tbl );
4575 for (GSList const *items = selection->itemList();
4576 items != NULL;
4577 items = items->next)
4578 {
4579 if (SP_IS_ARC((SPItem *) items->data)) {
4580 n_selected++;
4581 repr = SP_OBJECT_REPR((SPItem *) items->data);
4582 }
4583 }
4585 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4587 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4588 if (n_selected == 0) {
4589 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4590 } else if (n_selected == 1) {
4591 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4592 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4594 if (repr) {
4595 g_object_set_data( tbl, "repr", repr );
4596 Inkscape::GC::anchor(repr);
4597 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4598 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4599 }
4600 } else {
4601 // FIXME: implement averaging of all parameters for multiple selected
4602 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4603 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4604 sp_arctb_sensitivize( tbl, 1, 0 );
4605 }
4606 }
4609 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4610 {
4611 EgeAdjustmentAction* eact = 0;
4612 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4615 {
4616 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4617 ege_output_action_set_use_markup( act, TRUE );
4618 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4619 g_object_set_data( holder, "mode_action", act );
4620 }
4622 /* Start */
4623 {
4624 eact = create_adjustment_action( "ArcStartAction",
4625 _("Start"), _("Start:"),
4626 _("The angle (in degrees) from the horizontal to the arc's start point"),
4627 "tools.shapes.arc", "start", 0.0,
4628 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4629 -360.0, 360.0, 1.0, 10.0,
4630 0, 0, 0,
4631 sp_arctb_start_value_changed);
4632 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4633 }
4635 /* End */
4636 {
4637 eact = create_adjustment_action( "ArcEndAction",
4638 _("End"), _("End:"),
4639 _("The angle (in degrees) from the horizontal to the arc's end point"),
4640 "tools.shapes.arc", "end", 0.0,
4641 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4642 -360.0, 360.0, 1.0, 10.0,
4643 0, 0, 0,
4644 sp_arctb_end_value_changed);
4645 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4646 }
4648 /* Segments / Pie checkbox */
4649 {
4650 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4652 GtkTreeIter iter;
4653 gtk_list_store_append( model, &iter );
4654 gtk_list_store_set( model, &iter,
4655 0, _("Closed arc"),
4656 1, _("Switch to segment (closed shape with two radii)"),
4657 2, "circle_closed_arc",
4658 -1 );
4660 gtk_list_store_append( model, &iter );
4661 gtk_list_store_set( model, &iter,
4662 0, _("Open Arc"),
4663 1, _("Switch to arc (unclosed shape)"),
4664 2, "circle_open_arc",
4665 -1 );
4667 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4668 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4669 g_object_set_data( holder, "open_action", act );
4671 ege_select_one_action_set_appearance( act, "full" );
4672 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4673 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4674 ege_select_one_action_set_icon_column( act, 2 );
4675 ege_select_one_action_set_icon_size( act, secondarySize );
4676 ege_select_one_action_set_tooltip_column( act, 1 );
4678 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4679 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4680 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4681 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4682 }
4684 /* Make Whole */
4685 {
4686 InkAction* inky = ink_action_new( "ArcResetAction",
4687 _("Make whole"),
4688 _("Make the shape a whole ellipse, not arc or segment"),
4689 "reset_circle",
4690 secondarySize );
4691 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4692 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4693 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4694 g_object_set_data( holder, "make_whole", inky );
4695 }
4697 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4698 // sensitivize make whole and open checkbox
4699 {
4700 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4701 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4702 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4703 }
4706 sigc::connection *connection = new sigc::connection(
4707 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4708 );
4709 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4710 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4711 }
4716 // toggle button callbacks and updaters
4718 //########################
4719 //## Dropper ##
4720 //########################
4722 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4723 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4724 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4725 if ( set_action ) {
4726 if ( gtk_toggle_action_get_active( act ) ) {
4727 gtk_action_set_sensitive( set_action, TRUE );
4728 } else {
4729 gtk_action_set_sensitive( set_action, FALSE );
4730 }
4731 }
4733 spinbutton_defocus(GTK_OBJECT(tbl));
4734 }
4736 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4737 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4738 spinbutton_defocus(GTK_OBJECT(tbl));
4739 }
4742 /**
4743 * Dropper auxiliary toolbar construction and setup.
4744 *
4745 * TODO: Would like to add swatch of current color.
4746 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4747 * can drag and drop places. Will provide a nice mixing palette.
4748 */
4749 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4750 {
4751 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4753 {
4754 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4755 ege_output_action_set_use_markup( act, TRUE );
4756 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4757 }
4759 {
4760 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4761 _("Pick opacity"),
4762 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4763 NULL,
4764 Inkscape::ICON_SIZE_DECORATION );
4765 g_object_set( act, "short_label", _("Pick"), NULL );
4766 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4767 g_object_set_data( holder, "pick_action", act );
4768 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4769 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4770 }
4772 {
4773 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4774 _("Assign opacity"),
4775 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4776 NULL,
4777 Inkscape::ICON_SIZE_DECORATION );
4778 g_object_set( act, "short_label", _("Assign"), NULL );
4779 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4780 g_object_set_data( holder, "set_action", act );
4781 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4782 // make sure it's disabled if we're not picking alpha
4783 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4784 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4785 }
4786 }
4789 //########################
4790 //## LPETool ##
4791 //########################
4793 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4795 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4796 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4797 {
4798 using namespace Inkscape::LivePathEffect;
4800 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4801 SPEventContext *ec = desktop->event_context;
4802 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4803 return;
4804 }
4806 // only take action if run by the attr_changed listener
4807 if (!g_object_get_data(tbl, "freeze")) {
4808 // in turn, prevent listener from responding
4809 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4811 gint mode = ege_select_one_action_get_active(act);
4812 EffectType type = lpesubtools[mode];
4814 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4815 bool success = lpetool_try_construction(lc, type);
4816 if (success) {
4817 // since the construction was already performed, we set the state back to inactive
4818 ege_select_one_action_set_active(act, 0);
4819 mode = 0;
4820 } else {
4821 // switch to the chosen subtool
4822 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4823 }
4825 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4826 prefs_set_int_attribute( "tools.lpetool", "mode", mode );
4827 }
4829 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4830 }
4831 }
4833 void
4834 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4835 {
4836 using namespace Inkscape::LivePathEffect;
4837 {
4838 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4839 SPItem *item = selection->singleItem();
4840 SPEventContext *ec = selection->desktop()->event_context;
4841 if (!SP_IS_LPETOOL_CONTEXT(ec))
4842 return;
4843 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4844 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4845 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4846 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4847 if (lpe && lpe->effectType() == LINE_SEGMENT) {
4848 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4849 g_object_set_data(tbl, "currentlpe", lpe);
4850 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4851 gtk_action_set_sensitive(w, TRUE);
4852 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4853 } else {
4854 g_object_set_data(tbl, "currentlpe", NULL);
4855 g_object_set_data(tbl, "currentlpeitem", NULL);
4856 gtk_action_set_sensitive(w, FALSE);
4857 }
4858 } else {
4859 g_object_set_data(tbl, "currentlpe", NULL);
4860 g_object_set_data(tbl, "currentlpeitem", NULL);
4861 gtk_action_set_sensitive(w, FALSE);
4862 }
4863 }
4864 }
4866 static void
4867 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4868 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4870 bool show = gtk_toggle_action_get_active( act );
4871 prefs_set_int_attribute ("tools.lpetool", "show_bbox", show ? 1 : 0);
4873 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4874 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4875 lpetool_context_reset_limiting_bbox(lc);
4876 }
4877 }
4879 static void
4880 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4881 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4882 Inkscape::Selection *selection = desktop->selection;
4884 boost::optional<NR::Rect> bbox = selection->bounds();
4886 if (bbox) {
4887 Geom::Point A(bbox->min());
4888 Geom::Point B(bbox->max());
4890 A *= desktop->doc2dt();
4891 B *= desktop->doc2dt();
4893 // TODO: should we provide a way to store points in prefs?
4894 prefs_set_double_attribute ("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4895 prefs_set_double_attribute ("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4896 prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4897 prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4899 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4900 }
4902 gtk_toggle_action_set_active(act, false);
4903 }
4905 static void
4906 sp_line_segment_build_list(GObject *tbl)
4907 {
4908 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
4910 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
4911 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4912 gtk_list_store_clear (model);
4914 // TODO: we add the entries of rht combo box manually; later this should be done automatically
4915 {
4916 GtkTreeIter iter;
4917 gtk_list_store_append( model, &iter );
4918 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
4919 gtk_list_store_append( model, &iter );
4920 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
4921 gtk_list_store_append( model, &iter );
4922 gtk_list_store_set( model, &iter, 0, _("Open left"), 1, 1, -1 );
4923 gtk_list_store_append( model, &iter );
4924 gtk_list_store_set( model, &iter, 0, _("Open right"), 1, 2, -1 );
4925 }
4927 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
4928 }
4930 static void
4931 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
4932 using namespace Inkscape::LivePathEffect;
4934 // quit if run by the attr_changed listener
4935 if (g_object_get_data(tbl, "freeze")) {
4936 return;
4937 }
4939 // in turn, prevent listener from responding
4940 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4942 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
4943 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4944 if (lpeitem) {
4945 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4946 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
4947 sp_lpe_item_update_patheffect(lpeitem, true, true);
4948 }
4950 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4951 }
4953 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4954 {
4955 /** Automatically create a list of LPEs that get added to the toolbar **/
4956 {
4957 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4959 GtkTreeIter iter;
4961 // the first toggle button represents the state that no subtool is active (remove this when
4962 // this can be modeled by EgeSelectOneAction or some other action)
4963 gtk_list_store_append( model, &iter );
4964 gtk_list_store_set( model, &iter,
4965 0, _("All inactive"),
4966 1, _("No geometric tool is active"),
4967 2, _("all_inactive"),
4968 -1 );
4970 Inkscape::LivePathEffect::EffectType type;
4971 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
4972 type = lpesubtools[i];
4973 gtk_list_store_append( model, &iter );
4974 gtk_list_store_set( model, &iter,
4975 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4976 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4977 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4978 -1 );
4979 }
4981 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4982 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4983 g_object_set_data( holder, "lpetool_mode_action", act );
4985 ege_select_one_action_set_appearance( act, "full" );
4986 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4987 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4988 ege_select_one_action_set_icon_column( act, 2 );
4989 ege_select_one_action_set_tooltip_column( act, 1 );
4991 gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4992 ege_select_one_action_set_active( act, lpeToolMode );
4993 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4994 }
4996 /* Show limiting bounding box */
4997 {
4998 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
4999 _("Show limiting bounding box"),
5000 _("Show bounding box (used to cut infinite lines)"),
5001 "lpetool_show_bbox",
5002 Inkscape::ICON_SIZE_DECORATION );
5003 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5004 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5005 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
5006 }
5008 /* Set limiting bounding box to bbox of current selection */
5009 {
5010 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5011 _("Get limiting bounding box from selection"),
5012 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5013 "lpetool_set_bbox",
5014 Inkscape::ICON_SIZE_DECORATION );
5015 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5016 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5017 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5018 }
5021 /* Combo box to choose line segment type */
5022 {
5023 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5024 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5025 ege_select_one_action_set_appearance (act, "compact");
5026 g_object_set_data (holder, "lpetool_line_segment_action", act );
5028 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5030 sp_line_segment_build_list (holder);
5032 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5033 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5034 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5035 }
5037 //watch selection
5038 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5040 sigc::connection *c_selection_changed =
5041 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5042 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5043 pool->add_connection ("selection-changed", c_selection_changed);
5044 }
5046 //########################
5047 //## Eraser ##
5048 //########################
5050 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5051 {
5052 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5053 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5054 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5055 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
5056 }
5058 // only take action if run by the attr_changed listener
5059 if (!g_object_get_data( tbl, "freeze" )) {
5060 // in turn, prevent listener from responding
5061 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5063 if ( eraserMode != 0 ) {
5064 } else {
5065 }
5066 // TODO finish implementation
5068 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5069 }
5070 }
5072 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5073 {
5074 {
5075 /* Width */
5076 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5077 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5078 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5079 _("Pen Width"), _("Width:"),
5080 _("The width of the eraser pen (relative to the visible canvas area)"),
5081 "tools.eraser", "width", 15,
5082 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5083 1, 100, 1.0, 0.0,
5084 labels, values, G_N_ELEMENTS(labels),
5085 sp_ddc_width_value_changed, 0.01, 0, 100 );
5086 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5087 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5088 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5089 }
5091 {
5092 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5094 GtkTreeIter iter;
5095 gtk_list_store_append( model, &iter );
5096 gtk_list_store_set( model, &iter,
5097 0, _("Delete"),
5098 1, _("Delete objects touched by the eraser"),
5099 2, "delete_object",
5100 -1 );
5102 gtk_list_store_append( model, &iter );
5103 gtk_list_store_set( model, &iter,
5104 0, _("Cut"),
5105 1, _("Cut out from objects"),
5106 2, "difference",
5107 -1 );
5109 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5110 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5111 g_object_set_data( holder, "eraser_mode_action", act );
5113 ege_select_one_action_set_appearance( act, "full" );
5114 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5115 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5116 ege_select_one_action_set_icon_column( act, 2 );
5117 ege_select_one_action_set_tooltip_column( act, 1 );
5119 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5120 ege_select_one_action_set_active( act, eraserMode );
5121 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5122 }
5124 }
5126 //########################
5127 //## Text Toolbox ##
5128 //########################
5129 /*
5130 static void
5131 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5132 {
5133 //Call back for letter sizing spinbutton
5134 }
5136 static void
5137 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5138 {
5139 //Call back for line height spinbutton
5140 }
5142 static void
5143 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5144 {
5145 //Call back for horizontal kerning spinbutton
5146 }
5148 static void
5149 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5150 {
5151 //Call back for vertical kerning spinbutton
5152 }
5154 static void
5155 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5156 {
5157 //Call back for letter rotation spinbutton
5158 }*/
5160 namespace {
5162 bool popdown_visible = false;
5163 bool popdown_hasfocus = false;
5165 void
5166 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5167 {
5168 SPStyle *query =
5169 sp_style_new (SP_ACTIVE_DOCUMENT);
5171 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5173 int result_family =
5174 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5176 int result_style =
5177 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5179 int result_numbers =
5180 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5182 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5184 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5185 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5186 // there are no texts in selection, read from prefs
5188 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5189 if (repr) {
5190 sp_style_read_from_repr (query, repr);
5191 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5192 // do not reset the toolbar style from prefs if we already did it last time
5193 sp_style_unref(query);
5194 return;
5195 }
5196 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5197 } else {
5198 sp_style_unref(query);
5199 return;
5200 }
5201 } else {
5202 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5203 }
5205 if (query->text)
5206 {
5207 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5208 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5209 gtk_entry_set_text (GTK_ENTRY (entry), "");
5211 } else if (query->text->font_specification.value || query->text->font_family.value) {
5213 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5215 // Get the font that corresponds
5216 Glib::ustring familyName;
5218 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5219 if (font) {
5220 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5221 font->Unref();
5222 font = NULL;
5223 }
5225 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5227 Gtk::TreePath path;
5228 try {
5229 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5230 } catch (...) {
5231 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5232 sp_style_unref(query);
5233 return;
5234 }
5236 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5237 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5239 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5241 gtk_tree_selection_select_path (tselection, path.gobj());
5242 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5244 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5245 }
5247 //Size
5248 {
5249 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5250 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5251 g_object_set_data(tbl, "size-block", gpointer(1));
5252 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5253 g_object_set_data(tbl, "size-block", gpointer(0));
5254 g_free(str);
5255 }
5257 //Anchor
5258 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5259 {
5260 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5261 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5262 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5263 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5264 }
5265 else
5266 {
5267 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5268 {
5269 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5270 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5271 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5272 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5273 }
5274 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5275 {
5276 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5277 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5278 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5279 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5280 }
5281 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5282 {
5283 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5284 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5285 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5286 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5287 }
5288 }
5290 //Style
5291 {
5292 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5294 gboolean active = gtk_toggle_button_get_active (button);
5295 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5297 if (active != check)
5298 {
5299 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5300 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5301 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5302 }
5303 }
5305 {
5306 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5308 gboolean active = gtk_toggle_button_get_active (button);
5309 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5311 if (active != check)
5312 {
5313 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5314 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5315 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5316 }
5317 }
5319 //Orientation
5320 //locking both buttons, changing one affect all group (both)
5321 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5322 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5324 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5325 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5327 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5328 {
5329 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5330 }
5331 else
5332 {
5333 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5334 }
5335 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5336 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5337 }
5339 sp_style_unref(query);
5340 }
5342 void
5343 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5344 {
5345 sp_text_toolbox_selection_changed (selection, tbl);
5346 }
5348 void
5349 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5350 {
5351 sp_text_toolbox_selection_changed (NULL, tbl);
5352 }
5354 void
5355 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5356 GObject *tbl)
5357 {
5358 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5359 GtkTreeModel *model = 0;
5360 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5361 GtkTreeIter iter;
5362 char *family = 0;
5364 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5365 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5367 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5368 return;
5369 }
5371 gtk_tree_model_get (model, &iter, 0, &family, -1);
5373 if (g_object_get_data (G_OBJECT (selection), "block"))
5374 {
5375 gtk_entry_set_text (GTK_ENTRY (entry), family);
5376 return;
5377 }
5379 gtk_entry_set_text (GTK_ENTRY (entry), family);
5381 SPStyle *query =
5382 sp_style_new (SP_ACTIVE_DOCUMENT);
5384 int result_fontspec =
5385 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5387 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5389 SPCSSAttr *css = sp_repr_css_attr_new ();
5392 // First try to get the font spec from the stored value
5393 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5395 if (fontSpec.empty()) {
5396 // Construct a new font specification if it does not yet exist
5397 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5398 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5399 fontFromStyle->Unref();
5400 }
5402 if (!fontSpec.empty()) {
5403 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5404 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5405 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5406 if (font) {
5407 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5409 // Set all the these just in case they were altered when finding the best
5410 // match for the new family and old style...
5412 gchar c[256];
5414 font->Family(c, 256);
5415 sp_repr_css_set_property (css, "font-family", c);
5417 font->Attribute( "weight", c, 256);
5418 sp_repr_css_set_property (css, "font-weight", c);
5420 font->Attribute("style", c, 256);
5421 sp_repr_css_set_property (css, "font-style", c);
5423 font->Attribute("stretch", c, 256);
5424 sp_repr_css_set_property (css, "font-stretch", c);
5426 font->Attribute("variant", c, 256);
5427 sp_repr_css_set_property (css, "font-variant", c);
5429 font->Unref();
5430 }
5431 }
5432 }
5434 // If querying returned nothing, set the default style of the tool (for new texts)
5435 if (result_fontspec == QUERY_STYLE_NOTHING)
5436 {
5437 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5438 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5439 }
5440 else
5441 {
5442 sp_desktop_set_style (desktop, css, true, true);
5443 }
5445 sp_style_unref(query);
5447 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5448 _("Text: Change font family"));
5449 sp_repr_css_attr_unref (css);
5450 g_free(family);
5451 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5453 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5454 }
5456 /* This is where execution comes when the contents of the font family box have been completed
5457 by the press of the return key */
5458 void
5459 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5460 GObject *tbl)
5461 {
5462 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5464 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5465 try {
5466 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5467 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5468 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5469 gtk_tree_selection_select_path (selection, path.gobj());
5470 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5471 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5472 } catch (...) {
5473 if (family && strlen (family))
5474 {
5475 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5476 }
5477 }
5478 }
5480 void
5481 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5482 gpointer data)
5483 {
5484 if (g_object_get_data (G_OBJECT (button), "block")) return;
5485 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5486 int prop = GPOINTER_TO_INT(data);
5488 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5489 SPCSSAttr *css = sp_repr_css_attr_new ();
5491 switch (prop)
5492 {
5493 case 0:
5494 {
5495 sp_repr_css_set_property (css, "text-anchor", "start");
5496 sp_repr_css_set_property (css, "text-align", "start");
5497 break;
5498 }
5499 case 1:
5500 {
5501 sp_repr_css_set_property (css, "text-anchor", "middle");
5502 sp_repr_css_set_property (css, "text-align", "center");
5503 break;
5504 }
5506 case 2:
5507 {
5508 sp_repr_css_set_property (css, "text-anchor", "end");
5509 sp_repr_css_set_property (css, "text-align", "end");
5510 break;
5511 }
5513 case 3:
5514 {
5515 sp_repr_css_set_property (css, "text-anchor", "start");
5516 sp_repr_css_set_property (css, "text-align", "justify");
5517 break;
5518 }
5519 }
5521 SPStyle *query =
5522 sp_style_new (SP_ACTIVE_DOCUMENT);
5523 int result_numbers =
5524 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5526 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5527 if (result_numbers == QUERY_STYLE_NOTHING)
5528 {
5529 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5530 }
5532 sp_style_unref(query);
5534 sp_desktop_set_style (desktop, css, true, true);
5535 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5536 _("Text: Change alignment"));
5537 sp_repr_css_attr_unref (css);
5539 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5540 }
5542 void
5543 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5544 gpointer data)
5545 {
5546 if (g_object_get_data (G_OBJECT (button), "block")) return;
5548 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5549 SPCSSAttr *css = sp_repr_css_attr_new ();
5550 int prop = GPOINTER_TO_INT(data);
5551 bool active = gtk_toggle_button_get_active (button);
5553 SPStyle *query =
5554 sp_style_new (SP_ACTIVE_DOCUMENT);
5556 int result_fontspec =
5557 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5559 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5560 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5561 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5563 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5564 Glib::ustring newFontSpec = "";
5566 if (fontSpec.empty()) {
5567 // Construct a new font specification if it does not yet exist
5568 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5569 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5570 fontFromStyle->Unref();
5571 }
5573 switch (prop)
5574 {
5575 case 0:
5576 {
5577 if (!fontSpec.empty()) {
5578 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5579 }
5580 if (fontSpec != newFontSpec) {
5581 // Don't even set the bold if the font didn't exist on the system
5582 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5583 }
5584 break;
5585 }
5587 case 1:
5588 {
5589 if (!fontSpec.empty()) {
5590 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5591 }
5592 if (fontSpec != newFontSpec) {
5593 // Don't even set the italic if the font didn't exist on the system
5594 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5595 }
5596 break;
5597 }
5598 }
5600 if (!newFontSpec.empty()) {
5601 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5602 }
5604 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5605 if (result_fontspec == QUERY_STYLE_NOTHING)
5606 {
5607 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5608 }
5610 sp_style_unref(query);
5612 sp_desktop_set_style (desktop, css, true, true);
5613 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5614 _("Text: Change font style"));
5615 sp_repr_css_attr_unref (css);
5617 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5618 }
5620 void
5621 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5622 gpointer data)
5623 {
5624 if (g_object_get_data (G_OBJECT (button), "block")) {
5625 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5626 return;
5627 }
5629 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5630 SPCSSAttr *css = sp_repr_css_attr_new ();
5631 int prop = GPOINTER_TO_INT(data);
5633 switch (prop)
5634 {
5635 case 0:
5636 {
5637 sp_repr_css_set_property (css, "writing-mode", "lr");
5638 break;
5639 }
5641 case 1:
5642 {
5643 sp_repr_css_set_property (css, "writing-mode", "tb");
5644 break;
5645 }
5646 }
5648 SPStyle *query =
5649 sp_style_new (SP_ACTIVE_DOCUMENT);
5650 int result_numbers =
5651 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5653 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5654 if (result_numbers == QUERY_STYLE_NOTHING)
5655 {
5656 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5657 }
5659 sp_desktop_set_style (desktop, css, true, true);
5660 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5661 _("Text: Change orientation"));
5662 sp_repr_css_attr_unref (css);
5664 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5665 }
5667 gboolean
5668 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5669 {
5670 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5671 if (!desktop) return FALSE;
5673 switch (get_group0_keyval (event)) {
5674 case GDK_Escape: // defocus
5675 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5676 sp_text_toolbox_selection_changed (NULL, tbl); // update
5677 return TRUE; // I consumed the event
5678 break;
5679 }
5680 return FALSE;
5681 }
5683 gboolean
5684 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5685 {
5686 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5687 if (!desktop) return FALSE;
5689 switch (get_group0_keyval (event)) {
5690 case GDK_KP_Enter:
5691 case GDK_Return:
5692 case GDK_Escape: // defocus
5693 gtk_widget_hide (w);
5694 popdown_visible = false;
5695 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5696 return TRUE; // I consumed the event
5697 break;
5698 case GDK_w:
5699 case GDK_W:
5700 if (event->state & GDK_CONTROL_MASK) {
5701 gtk_widget_hide (w);
5702 popdown_visible = false;
5703 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5704 return TRUE; // I consumed the event
5705 }
5706 break;
5707 }
5708 return FALSE;
5709 }
5712 void
5713 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5714 GObject *tbl)
5715 {
5716 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5718 if (g_object_get_data (tbl, "size-block")) return;
5720 // If this is not from selecting a size in the list (in which case get_active will give the
5721 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5722 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5723 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5724 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5725 return;
5727 gdouble value = -1;
5728 {
5729 gchar *endptr;
5730 gchar *const text = gtk_combo_box_get_active_text(cbox);
5731 if (text) {
5732 value = g_strtod(text, &endptr);
5733 if (endptr == text) { // Conversion failed, non-numeric input.
5734 value = -1;
5735 }
5736 g_free(text);
5737 }
5738 }
5739 if (value <= 0) {
5740 return; // could not parse value
5741 }
5743 SPCSSAttr *css = sp_repr_css_attr_new ();
5744 Inkscape::CSSOStringStream osfs;
5745 osfs << value;
5746 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5748 SPStyle *query =
5749 sp_style_new (SP_ACTIVE_DOCUMENT);
5750 int result_numbers =
5751 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5753 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5754 if (result_numbers == QUERY_STYLE_NOTHING)
5755 {
5756 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5757 }
5759 sp_style_unref(query);
5761 sp_desktop_set_style (desktop, css, true, true);
5762 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5763 _("Text: Change font size"));
5764 sp_repr_css_attr_unref (css);
5766 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5767 }
5769 gboolean
5770 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5771 {
5772 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5773 if (!desktop) return FALSE;
5775 if (!g_object_get_data (tbl, "esc-pressed")) {
5776 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5777 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5778 sp_text_toolbox_size_changed (cbox, tbl);
5779 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5780 }
5781 return FALSE; // I consumed the event
5782 }
5785 gboolean
5786 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5787 {
5788 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5789 if (!desktop) return FALSE;
5791 switch (get_group0_keyval (event)) {
5792 case GDK_Escape: // defocus
5793 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5794 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5795 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5796 return TRUE; // I consumed the event
5797 break;
5798 case GDK_Return: // defocus
5799 case GDK_KP_Enter:
5800 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5801 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5802 sp_text_toolbox_size_changed (cbox, tbl);
5803 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5804 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5805 return TRUE; // I consumed the event
5806 break;
5807 }
5808 return FALSE;
5809 }
5811 void
5812 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5813 GObject *tbl)
5814 {
5815 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5816 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5817 int x, y;
5819 if (!popdown_visible)
5820 {
5821 gdk_window_get_origin (widget->window, &x, &y);
5822 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5823 gtk_widget_show_all (popdown);
5824 //sp_transientize (popdown);
5826 gdk_pointer_grab (widget->window, TRUE,
5827 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5828 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5829 GDK_POINTER_MOTION_MASK),
5830 NULL, NULL, GDK_CURRENT_TIME);
5832 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5834 popdown_visible = true;
5835 }
5836 else
5837 {
5838 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5839 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5840 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5841 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5842 gtk_widget_hide (popdown);
5843 popdown_visible = false;
5844 }
5845 }
5847 gboolean
5848 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5849 GdkEventFocus */*event*/,
5850 GObject */*tbl*/)
5851 {
5852 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5853 return FALSE;
5854 }
5856 gboolean
5857 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5858 GdkEventFocus */*event*/,
5859 GObject */*tbl*/)
5860 {
5861 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5863 if (popdown_hasfocus) {
5864 gtk_widget_hide (popdown);
5865 popdown_hasfocus = false;
5866 popdown_visible = false;
5867 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5868 return TRUE;
5869 }
5870 return FALSE;
5871 }
5873 gboolean
5874 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5875 GdkEventFocus */*event*/,
5876 GObject */*tbl*/)
5877 {
5878 popdown_hasfocus = true;
5879 return TRUE;
5880 }
5883 void
5884 cell_data_func (GtkTreeViewColumn */*column*/,
5885 GtkCellRenderer *cell,
5886 GtkTreeModel *tree_model,
5887 GtkTreeIter *iter,
5888 gpointer /*data*/)
5889 {
5890 gchar *family;
5891 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5892 gchar *const family_escaped = g_markup_escape_text(family, -1);
5894 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5895 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5897 std::stringstream markup;
5898 markup << family_escaped << " <span foreground='darkgray' font_family='"
5899 << family_escaped << "'>" << sample_escaped << "</span>";
5900 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5902 g_free(family);
5903 g_free(family_escaped);
5904 g_free(sample_escaped);
5905 }
5907 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5908 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5909 if (completion) {
5910 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5911 g_object_unref (completion);
5912 }
5913 }
5915 GtkWidget*
5916 sp_text_toolbox_new (SPDesktop *desktop)
5917 {
5918 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5919 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5921 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5922 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5924 GtkTooltips *tt = gtk_tooltips_new();
5925 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5927 ////////////Family
5928 //Window
5929 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5930 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5932 //Entry
5933 GtkWidget *entry = gtk_entry_new ();
5934 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5935 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5936 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5937 gtk_entry_completion_set_text_column (completion, 0);
5938 gtk_entry_completion_set_minimum_key_length (completion, 1);
5939 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5940 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5941 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5942 gtk_toolbar_append_widget( tbl, entry, "", "" );
5943 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5945 //Button
5946 GtkWidget *button = gtk_button_new ();
5947 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5948 gtk_toolbar_append_widget( tbl, button, "", "");
5950 //Popdown
5951 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5952 GtkWidget *treeview = gtk_tree_view_new ();
5954 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5955 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5956 gtk_tree_view_column_pack_start (column, cell, FALSE);
5957 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5958 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5959 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5961 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5962 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5963 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5965 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5967 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5968 gtk_container_add (GTK_CONTAINER (sw), treeview);
5970 gtk_container_add (GTK_CONTAINER (window), sw);
5971 gtk_widget_set_size_request (window, 300, 450);
5973 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5974 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5975 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5977 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5979 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5980 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5981 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5983 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5984 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5986 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5987 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5988 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5989 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5990 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5992 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5993 GtkWidget *box = gtk_event_box_new ();
5994 gtk_container_add (GTK_CONTAINER (box), image);
5995 gtk_toolbar_append_widget( tbl, box, "", "");
5996 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5997 GtkTooltips *tooltips = gtk_tooltips_new ();
5998 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5999 gtk_widget_hide (GTK_WIDGET (box));
6000 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6002 ////////////Size
6003 gchar const *const sizes[] = {
6004 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6005 "16", "18", "20", "22", "24", "28",
6006 "32", "36", "40", "48", "56", "64", "72", "144"
6007 };
6009 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6010 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6011 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6012 }
6013 gtk_widget_set_size_request (cbox, 80, -1);
6014 gtk_toolbar_append_widget( tbl, cbox, "", "");
6015 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6016 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6017 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6018 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6020 ////////////Text anchor
6021 GtkWidget *group = gtk_radio_button_new (NULL);
6022 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6023 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6025 // left
6026 GtkWidget *rbutton = group;
6027 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6028 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6029 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6031 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6032 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6033 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6034 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6036 // center
6037 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6038 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6039 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6040 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6042 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6043 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6044 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6045 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6047 // right
6048 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6049 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6050 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6051 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6053 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6054 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6055 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6056 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6058 // fill
6059 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6060 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6061 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6062 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6064 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6065 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6066 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6067 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6069 gtk_toolbar_append_widget( tbl, row, "", "");
6071 //spacer
6072 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6074 ////////////Text style
6075 row = gtk_hbox_new (FALSE, 4);
6077 // bold
6078 rbutton = gtk_toggle_button_new ();
6079 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6080 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6081 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6082 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6084 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6085 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6086 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6088 // italic
6089 rbutton = gtk_toggle_button_new ();
6090 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6091 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6092 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6093 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6095 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6096 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6097 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6099 gtk_toolbar_append_widget( tbl, row, "", "");
6101 //spacer
6102 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6104 ////////////Text orientation
6105 group = gtk_radio_button_new (NULL);
6106 row = gtk_hbox_new (FALSE, 4);
6107 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6109 // horizontal
6110 rbutton = group;
6111 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6112 gtk_container_add (GTK_CONTAINER (rbutton),
6113 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6114 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6115 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6117 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6118 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6119 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6121 // vertical
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),
6125 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6126 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6127 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6129 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6130 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6131 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6132 gtk_toolbar_append_widget( tbl, row, "", "" );
6135 //watch selection
6136 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6138 sigc::connection *c_selection_changed =
6139 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6140 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6141 pool->add_connection ("selection-changed", c_selection_changed);
6143 sigc::connection *c_selection_modified =
6144 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6145 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6146 pool->add_connection ("selection-modified", c_selection_modified);
6148 sigc::connection *c_subselection_changed =
6149 new sigc::connection (desktop->connectToolSubselectionChanged
6150 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6151 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6153 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6156 gtk_widget_show_all( GTK_WIDGET(tbl) );
6158 return GTK_WIDGET(tbl);
6159 } // end of sp_text_toolbox_new()
6161 }//<unnamed> namespace
6164 //#########################
6165 //## Connector ##
6166 //#########################
6168 static void sp_connector_path_set_avoid(void)
6169 {
6170 cc_selection_set_avoid(true);
6171 }
6174 static void sp_connector_path_set_ignore(void)
6175 {
6176 cc_selection_set_avoid(false);
6177 }
6181 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6182 {
6183 // quit if run by the _changed callbacks
6184 if (g_object_get_data( tbl, "freeze" )) {
6185 return;
6186 }
6188 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6189 SPDocument *doc = sp_desktop_document(desktop);
6191 if (!sp_document_get_undo_sensitive(doc))
6192 {
6193 return;
6194 }
6196 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6198 if ( repr->attribute("inkscape:connector-spacing") ) {
6199 gdouble priorValue = gtk_adjustment_get_value(adj);
6200 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6201 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6202 return;
6203 }
6204 } else if ( adj->value == defaultConnSpacing ) {
6205 return;
6206 }
6208 // in turn, prevent callbacks from responding
6209 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6211 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6212 SP_OBJECT(desktop->namedview)->updateRepr();
6214 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6215 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6216 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6217 NR::Matrix m = NR::identity();
6218 avoid_item_move(&m, item);
6219 }
6221 if (items) {
6222 g_slist_free(items);
6223 }
6225 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6226 _("Change connector spacing"));
6228 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6230 spinbutton_defocus(GTK_OBJECT(tbl));
6231 }
6233 static void sp_connector_graph_layout(void)
6234 {
6235 if (!SP_ACTIVE_DESKTOP) return;
6237 // hack for clones, see comment in align-and-distribute.cpp
6238 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6239 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6241 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6243 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6245 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6246 }
6248 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6249 {
6250 if ( gtk_toggle_action_get_active( act ) ) {
6251 prefs_set_string_attribute("tools.connector", "directedlayout",
6252 "true");
6253 } else {
6254 prefs_set_string_attribute("tools.connector", "directedlayout",
6255 "false");
6256 }
6257 }
6259 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6260 {
6261 if ( gtk_toggle_action_get_active( act ) ) {
6262 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6263 "true");
6264 } else {
6265 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6266 "false");
6267 }
6268 }
6271 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6272 {
6273 prefs_set_double_attribute("tools.connector", "length", adj->value);
6274 spinbutton_defocus(GTK_OBJECT(tbl));
6275 }
6277 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6278 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6279 bool /*is_interactive*/, gpointer data)
6280 {
6281 GtkWidget *tbl = GTK_WIDGET(data);
6283 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6284 return;
6285 }
6286 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6287 return;
6288 }
6290 GtkAdjustment *adj = (GtkAdjustment*)
6291 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6292 gdouble spacing = defaultConnSpacing;
6293 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6295 gtk_adjustment_set_value(adj, spacing);
6296 }
6299 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6300 NULL, /* child_added */
6301 NULL, /* child_removed */
6302 connector_tb_event_attr_changed,
6303 NULL, /* content_changed */
6304 NULL /* order_changed */
6305 };
6308 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6309 {
6310 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6312 {
6313 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6314 _("Avoid"),
6315 _("Make connectors avoid selected objects"),
6316 "connector_avoid",
6317 secondarySize );
6318 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6319 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6320 }
6322 {
6323 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6324 _("Ignore"),
6325 _("Make connectors ignore selected objects"),
6326 "connector_ignore",
6327 secondarySize );
6328 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6329 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6330 }
6332 EgeAdjustmentAction* eact = 0;
6334 // Spacing spinbox
6335 eact = create_adjustment_action( "ConnectorSpacingAction",
6336 _("Connector Spacing"), _("Spacing:"),
6337 _("The amount of space left around objects by auto-routing connectors"),
6338 "tools.connector", "spacing", defaultConnSpacing,
6339 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6340 0, 100, 1.0, 10.0,
6341 0, 0, 0,
6342 connector_spacing_changed, 1, 0 );
6343 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6345 // Graph (connector network) layout
6346 {
6347 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6348 _("Graph"),
6349 _("Nicely arrange selected connector network"),
6350 "graph_layout",
6351 secondarySize );
6352 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6353 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6354 }
6356 // Default connector length spinbox
6357 eact = create_adjustment_action( "ConnectorLengthAction",
6358 _("Connector Length"), _("Length:"),
6359 _("Ideal length for connectors when layout is applied"),
6360 "tools.connector", "length", 100,
6361 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6362 10, 1000, 10.0, 100.0,
6363 0, 0, 0,
6364 connector_length_changed, 1, 0 );
6365 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6368 // Directed edges toggle button
6369 {
6370 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6371 _("Downwards"),
6372 _("Make connectors with end-markers (arrows) point downwards"),
6373 "directed_graph",
6374 Inkscape::ICON_SIZE_DECORATION );
6375 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6377 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6378 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6379 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6381 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6382 }
6384 // Avoid overlaps toggle button
6385 {
6386 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6387 _("Remove overlaps"),
6388 _("Do not allow overlapping shapes"),
6389 "remove_overlaps",
6390 Inkscape::ICON_SIZE_DECORATION );
6391 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6393 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6394 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6395 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6397 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6398 }
6400 // Code to watch for changes to the connector-spacing attribute in
6401 // the XML.
6402 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6403 g_assert(repr != NULL);
6405 purge_repr_listener( holder, holder );
6407 if (repr) {
6408 g_object_set_data( holder, "repr", repr );
6409 Inkscape::GC::anchor(repr);
6410 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6411 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6412 }
6413 } // end of sp_connector_toolbox_prep()
6416 //#########################
6417 //## Paintbucket ##
6418 //#########################
6420 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6421 {
6422 gint channels = ege_select_one_action_get_active( act );
6423 flood_channels_set_channels( channels );
6424 }
6426 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6427 {
6428 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6429 }
6431 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6432 {
6433 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6434 }
6436 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6437 {
6438 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6439 SPUnit const *unit = tracker->getActiveUnit();
6441 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6443 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6444 }
6446 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6447 {
6448 // FIXME: make defaults settable via Inkscape Options
6449 struct KeyValue {
6450 char const *key;
6451 double value;
6452 } const key_values[] = {
6453 {"threshold", 15},
6454 {"offset", 0.0}
6455 };
6457 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6458 KeyValue const &kv = key_values[i];
6459 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6460 if ( adj ) {
6461 gtk_adjustment_set_value(adj, kv.value);
6462 }
6463 }
6465 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6466 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6467 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6468 ege_select_one_action_set_active( autogap_action, 0 );
6469 }
6471 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6472 {
6473 EgeAdjustmentAction* eact = 0;
6475 {
6476 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6478 GList* items = 0;
6479 gint count = 0;
6480 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6481 {
6482 GtkTreeIter iter;
6483 gtk_list_store_append( model, &iter );
6484 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6485 count++;
6486 }
6487 g_list_free( items );
6488 items = 0;
6489 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6490 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6491 ege_select_one_action_set_appearance( act1, "compact" );
6492 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6493 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6494 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6495 g_object_set_data( holder, "channels_action", act1 );
6496 }
6498 // Spacing spinbox
6499 {
6500 eact = create_adjustment_action(
6501 "ThresholdAction",
6502 _("Fill Threshold"), _("Threshold:"),
6503 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6504 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6505 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6506 0, 0, 0,
6507 paintbucket_threshold_changed, 1, 0 );
6509 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6510 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6511 }
6513 // Create the units menu.
6514 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6515 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6516 if (stored_unit)
6517 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6518 g_object_set_data( holder, "tracker", tracker );
6519 {
6520 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6521 gtk_action_group_add_action( mainActions, act );
6522 }
6524 // Offset spinbox
6525 {
6526 eact = create_adjustment_action(
6527 "OffsetAction",
6528 _("Grow/shrink by"), _("Grow/shrink by:"),
6529 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6530 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6531 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6532 0, 0, 0,
6533 paintbucket_offset_changed, 1, 2);
6534 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6536 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6537 }
6539 /* Auto Gap */
6540 {
6541 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6543 GList* items = 0;
6544 gint count = 0;
6545 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6546 {
6547 GtkTreeIter iter;
6548 gtk_list_store_append( model, &iter );
6549 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6550 count++;
6551 }
6552 g_list_free( items );
6553 items = 0;
6554 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6555 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6556 ege_select_one_action_set_appearance( act2, "compact" );
6557 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6558 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6559 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6560 g_object_set_data( holder, "autogap_action", act2 );
6561 }
6563 /* Reset */
6564 {
6565 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6566 _("Defaults"),
6567 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6568 GTK_STOCK_CLEAR );
6569 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6570 gtk_action_group_add_action( mainActions, act );
6571 gtk_action_set_sensitive( act, TRUE );
6572 }
6574 }
6576 /*
6577 Local Variables:
6578 mode:c++
6579 c-file-style:"stroustrup"
6580 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6581 indent-tabs-mode:nil
6582 fill-column:99
6583 End:
6584 */
6585 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :