ab2153fe0386722c343f0cf982bbaec7ff9876b5
1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 * Maximilian Albert <maximilian.albert@gmail.com>
18 *
19 * Copyright (C) 2004 David Turner
20 * Copyright (C) 2003 MenTaLguY
21 * Copyright (C) 1999-2008 authors
22 * Copyright (C) 2001-2002 Ximian, Inc.
23 *
24 * Released under GNU GPL, read the file 'COPYING' for more information
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <cstring>
32 #include <string>
34 #include <gtkmm.h>
35 #include <gtk/gtk.h>
36 #include <iostream>
37 #include <sstream>
39 #include "widgets/button.h"
40 #include "widgets/widget-sizes.h"
41 #include "widgets/spw-utilities.h"
42 #include "widgets/spinbutton-events.h"
43 #include "dialogs/text-edit.h"
44 #include "dialogs/dialog-events.h"
46 #include "ui/widget/style-swatch.h"
48 #include "prefs-utils.h"
49 #include "verbs.h"
50 #include "sp-namedview.h"
51 #include "desktop.h"
52 #include "desktop-handles.h"
53 #include "xml/repr.h"
54 #include "xml/node-event-vector.h"
55 #include "xml/attribute-record.h"
56 #include <glibmm/i18n.h>
57 #include "helper/unit-menu.h"
58 #include "helper/units.h"
59 #include "live_effects/effect.h"
61 #include "inkscape.h"
62 #include "conn-avoid-ref.h"
65 #include "select-toolbar.h"
66 #include "gradient-toolbar.h"
68 #include "connector-context.h"
69 #include "node-context.h"
70 #include "pen-context.h"
71 #include "lpe-tool-context.h"
72 #include "live_effects/lpe-line_segment.h"
73 #include "shape-editor.h"
74 #include "tweak-context.h"
75 #include "sp-rect.h"
76 #include "box3d.h"
77 #include "box3d-context.h"
78 #include "sp-star.h"
79 #include "sp-spiral.h"
80 #include "sp-ellipse.h"
81 #include "sp-text.h"
82 #include "sp-flowtext.h"
83 #include "sp-clippath.h"
84 #include "sp-mask.h"
85 #include "style.h"
86 #include "tools-switch.h"
87 #include "selection.h"
88 #include "selection-chemistry.h"
89 #include "document-private.h"
90 #include "desktop-style.h"
91 #include "../libnrtype/font-lister.h"
92 #include "../libnrtype/font-instance.h"
93 #include "../connection-pool.h"
94 #include "../prefs-utils.h"
95 #include "../inkscape-stock.h"
96 #include "icon.h"
97 #include "graphlayout/graphlayout.h"
98 #include "interface.h"
99 #include "shortcuts.h"
101 #include "mod360.h"
103 #include "toolbox.h"
105 #include "flood-context.h"
107 #include "ink-action.h"
108 #include "ege-adjustment-action.h"
109 #include "ege-output-action.h"
110 #include "ege-select-one-action.h"
111 #include "helper/unit-tracker.h"
112 #include "live_effects/lpe-angle_bisector.h"
114 #include "svg/css-ostringstream.h"
116 #include "widgets/calligraphic-profile-rename.h"
118 using Inkscape::UnitTracker;
120 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
121 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
123 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
136 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
139 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
141 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
144 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
145 static Inkscape::IconSize sizeChoices[] = {
146 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
147 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
148 Inkscape::ICON_SIZE_MENU
149 };
150 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
151 return sizeChoices[index];
152 }
154 static struct {
155 gchar const *type_name;
156 gchar const *data_name;
157 sp_verb_t verb;
158 sp_verb_t doubleclick_verb;
159 } const tools[] = {
160 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
161 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
162 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
163 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
164 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
165 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
166 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
167 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
168 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
169 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
170 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
171 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
172 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
173 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
174 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
175 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
176 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
177 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
178 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
179 { NULL, NULL, 0, 0 }
180 };
182 static struct {
183 gchar const *type_name;
184 gchar const *data_name;
185 GtkWidget *(*create_func)(SPDesktop *desktop);
186 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
187 gchar const *ui_name;
188 gint swatch_verb_id;
189 gchar const *swatch_tool;
190 gchar const *swatch_tip;
191 } const aux_toolboxes[] = {
192 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
193 SP_VERB_INVALID, 0, 0},
194 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
195 SP_VERB_INVALID, 0, 0},
196 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
197 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
198 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
199 SP_VERB_INVALID, 0, 0},
200 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
201 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
202 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
203 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
204 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
205 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
206 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
207 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
208 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
209 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
210 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
211 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
212 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
213 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
214 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
215 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
216 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
217 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
218 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
219 SP_VERB_CONTEXT_LPETOOL_PREFS, "tools.lpetool", _("TBD")},
220 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
221 SP_VERB_INVALID, 0, 0},
222 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
223 SP_VERB_INVALID, 0, 0},
224 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
225 SP_VERB_INVALID, 0, 0},
226 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
227 SP_VERB_INVALID, 0, 0},
228 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
229 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
230 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
231 };
233 #define TOOLBAR_SLIDER_HINT "full"
235 static gchar const * ui_descr =
236 "<ui>"
237 " <toolbar name='SelectToolbar'>"
238 " <toolitem action='EditSelectAll' />"
239 " <toolitem action='EditSelectAllInAllLayers' />"
240 " <toolitem action='EditDeselect' />"
241 " <separator />"
242 " <toolitem action='ObjectRotate90CCW' />"
243 " <toolitem action='ObjectRotate90' />"
244 " <toolitem action='ObjectFlipHorizontally' />"
245 " <toolitem action='ObjectFlipVertically' />"
246 " <separator />"
247 " <toolitem action='SelectionToBack' />"
248 " <toolitem action='SelectionLower' />"
249 " <toolitem action='SelectionRaise' />"
250 " <toolitem action='SelectionToFront' />"
251 " <separator />"
252 " <toolitem action='XAction' />"
253 " <toolitem action='YAction' />"
254 " <toolitem action='WidthAction' />"
255 " <toolitem action='LockAction' />"
256 " <toolitem action='HeightAction' />"
257 " <toolitem action='UnitsAction' />"
258 " <separator />"
259 " <toolitem action='transform_affect_label' />"
260 " <toolitem action='transform_stroke' />"
261 " <toolitem action='transform_corners' />"
262 " <toolitem action='transform_gradient' />"
263 " <toolitem action='transform_pattern' />"
264 " </toolbar>"
266 " <toolbar name='NodeToolbar'>"
267 " <toolitem action='NodeInsertAction' />"
268 " <toolitem action='NodeDeleteAction' />"
269 " <separator />"
270 " <toolitem action='NodeJoinAction' />"
271 " <toolitem action='NodeBreakAction' />"
272 " <separator />"
273 " <toolitem action='NodeJoinSegmentAction' />"
274 " <toolitem action='NodeDeleteSegmentAction' />"
275 " <separator />"
276 " <toolitem action='NodeCuspAction' />"
277 " <toolitem action='NodeSmoothAction' />"
278 " <toolitem action='NodeSymmetricAction' />"
279 " <separator />"
280 " <toolitem action='NodeLineAction' />"
281 " <toolitem action='NodeCurveAction' />"
282 " <separator />"
283 " <toolitem action='ObjectToPath' />"
284 " <toolitem action='StrokeToPath' />"
285 " <separator />"
286 " <toolitem action='NodeXAction' />"
287 " <toolitem action='NodeYAction' />"
288 " <toolitem action='NodeUnitsAction' />"
289 " <separator />"
290 " <toolitem action='ObjectEditClipPathAction' />"
291 " <toolitem action='ObjectEditMaskPathAction' />"
292 " <toolitem action='EditNextLPEParameterAction' />"
293 " <separator />"
294 " <toolitem action='NodesShowHandlesAction' />"
295 " <toolitem action='NodesShowHelperpath' />"
296 " </toolbar>"
298 " <toolbar name='TweakToolbar'>"
299 " <toolitem action='TweakWidthAction' />"
300 " <separator />"
301 " <toolitem action='TweakForceAction' />"
302 " <toolitem action='TweakPressureAction' />"
303 " <separator />"
304 " <toolitem action='TweakModeAction' />"
305 " <separator />"
306 " <toolitem action='TweakFidelityAction' />"
307 " <separator />"
308 " <toolitem action='TweakChannelsLabel' />"
309 " <toolitem action='TweakDoH' />"
310 " <toolitem action='TweakDoS' />"
311 " <toolitem action='TweakDoL' />"
312 " <toolitem action='TweakDoO' />"
313 " </toolbar>"
315 " <toolbar name='ZoomToolbar'>"
316 " <toolitem action='ZoomIn' />"
317 " <toolitem action='ZoomOut' />"
318 " <separator />"
319 " <toolitem action='Zoom1:0' />"
320 " <toolitem action='Zoom1:2' />"
321 " <toolitem action='Zoom2:1' />"
322 " <separator />"
323 " <toolitem action='ZoomSelection' />"
324 " <toolitem action='ZoomDrawing' />"
325 " <toolitem action='ZoomPage' />"
326 " <toolitem action='ZoomPageWidth' />"
327 " <separator />"
328 " <toolitem action='ZoomPrev' />"
329 " <toolitem action='ZoomNext' />"
330 " </toolbar>"
332 " <toolbar name='StarToolbar'>"
333 " <separator />"
334 " <toolitem action='StarStateAction' />"
335 " <separator />"
336 " <toolitem action='FlatAction' />"
337 " <separator />"
338 " <toolitem action='MagnitudeAction' />"
339 " <toolitem action='SpokeAction' />"
340 " <toolitem action='RoundednessAction' />"
341 " <toolitem action='RandomizationAction' />"
342 " <separator />"
343 " <toolitem action='StarResetAction' />"
344 " </toolbar>"
346 " <toolbar name='RectToolbar'>"
347 " <toolitem action='RectStateAction' />"
348 " <toolitem action='RectWidthAction' />"
349 " <toolitem action='RectHeightAction' />"
350 " <toolitem action='RadiusXAction' />"
351 " <toolitem action='RadiusYAction' />"
352 " <toolitem action='RectUnitsAction' />"
353 " <separator />"
354 " <toolitem action='RectResetAction' />"
355 " </toolbar>"
357 " <toolbar name='3DBoxToolbar'>"
358 " <toolitem action='3DBoxAngleXAction' />"
359 " <toolitem action='3DBoxVPXStateAction' />"
360 " <separator />"
361 " <toolitem action='3DBoxAngleYAction' />"
362 " <toolitem action='3DBoxVPYStateAction' />"
363 " <separator />"
364 " <toolitem action='3DBoxAngleZAction' />"
365 " <toolitem action='3DBoxVPZStateAction' />"
366 " </toolbar>"
368 " <toolbar name='SpiralToolbar'>"
369 " <toolitem action='SpiralStateAction' />"
370 " <toolitem action='SpiralRevolutionAction' />"
371 " <toolitem action='SpiralExpansionAction' />"
372 " <toolitem action='SpiralT0Action' />"
373 " <separator />"
374 " <toolitem action='SpiralResetAction' />"
375 " </toolbar>"
377 " <toolbar name='PenToolbar'>"
378 " <toolitem action='FreehandModeActionPen' />"
379 " <separator />"
380 " <toolitem action='SetPenShapeAction'/>"
381 " </toolbar>"
383 " <toolbar name='PencilToolbar'>"
384 " <toolitem action='FreehandModeActionPencil' />"
385 " <separator />"
386 " <toolitem action='PencilToleranceAction' />"
387 " <separator />"
388 " <toolitem action='PencilResetAction' />"
389 " <separator />"
390 " <toolitem action='SetPencilShapeAction'/>"
391 " </toolbar>"
393 " <toolbar name='CalligraphyToolbar'>"
394 " <separator />"
395 " <toolitem action='SetProfileAction'/>"
396 " <separator />"
397 " <toolitem action='CalligraphyWidthAction' />"
398 " <toolitem action='PressureAction' />"
399 " <toolitem action='TraceAction' />"
400 " <toolitem action='ThinningAction' />"
401 " <separator />"
402 " <toolitem action='AngleAction' />"
403 " <toolitem action='TiltAction' />"
404 " <toolitem action='FixationAction' />"
405 " <separator />"
406 " <toolitem action='CapRoundingAction' />"
407 " <separator />"
408 " <toolitem action='TremorAction' />"
409 " <toolitem action='WiggleAction' />"
410 " <toolitem action='MassAction' />"
411 " <separator />"
412 " </toolbar>"
414 " <toolbar name='ArcToolbar'>"
415 " <toolitem action='ArcStateAction' />"
416 " <separator />"
417 " <toolitem action='ArcStartAction' />"
418 " <toolitem action='ArcEndAction' />"
419 " <separator />"
420 " <toolitem action='ArcOpenAction' />"
421 " <separator />"
422 " <toolitem action='ArcResetAction' />"
423 " <separator />"
424 " </toolbar>"
426 " <toolbar name='PaintbucketToolbar'>"
427 " <toolitem action='ChannelsAction' />"
428 " <separator />"
429 " <toolitem action='ThresholdAction' />"
430 " <separator />"
431 " <toolitem action='OffsetAction' />"
432 " <toolitem action='PaintbucketUnitsAction' />"
433 " <separator />"
434 " <toolitem action='AutoGapAction' />"
435 " <separator />"
436 " <toolitem action='PaintbucketResetAction' />"
437 " </toolbar>"
439 " <toolbar name='EraserToolbar'>"
440 " <toolitem action='EraserWidthAction' />"
441 " <separator />"
442 " <toolitem action='EraserModeAction' />"
443 " </toolbar>"
445 " <toolbar name='LPEToolToolbar'>"
446 " <toolitem action='LPEToolModeAction' />"
447 " <separator />"
448 " <toolitem action='LPEShowBBoxAction' />"
449 " <toolitem action='LPEBBoxFromSelectionAction' />"
450 " <separator />"
451 " <toolitem action='LPELineSegmentAction' />"
452 " <separator />"
453 " <toolitem action='LPEMeasuringAction' />"
454 " <toolitem action='LPEToolUnitsAction' />"
455 " <separator />"
456 " <toolitem action='LPEOpenLPEDialogAction' />"
457 " </toolbar>"
459 " <toolbar name='DropperToolbar'>"
460 " <toolitem action='DropperOpacityAction' />"
461 " <toolitem action='DropperPickAlphaAction' />"
462 " <toolitem action='DropperSetAlphaAction' />"
463 " </toolbar>"
465 " <toolbar name='ConnectorToolbar'>"
466 " <toolitem action='ConnectorAvoidAction' />"
467 " <toolitem action='ConnectorIgnoreAction' />"
468 " <toolitem action='ConnectorSpacingAction' />"
469 " <toolitem action='ConnectorGraphAction' />"
470 " <toolitem action='ConnectorLengthAction' />"
471 " <toolitem action='ConnectorDirectedAction' />"
472 " <toolitem action='ConnectorOverlapAction' />"
473 " </toolbar>"
475 "</ui>"
476 ;
478 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
480 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
482 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
483 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
485 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
486 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
488 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
489 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
492 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
493 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
494 Inkscape::UI::View::View *view, GtkTooltips *tt);
496 class VerbAction : public Gtk::Action {
497 public:
498 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
500 virtual ~VerbAction();
501 virtual void set_active(bool active = true);
503 protected:
504 virtual Gtk::Widget* create_menu_item_vfunc();
505 virtual Gtk::Widget* create_tool_item_vfunc();
507 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
508 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
510 virtual void on_activate();
512 private:
513 Inkscape::Verb* verb;
514 Inkscape::Verb* verb2;
515 Inkscape::UI::View::View *view;
516 GtkTooltips *tooltips;
517 bool active;
519 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
520 };
523 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
524 {
525 Glib::RefPtr<VerbAction> result;
526 SPAction *action = verb->get_action(view);
527 if ( action ) {
528 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
529 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
530 }
532 return result;
533 }
535 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
536 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
537 verb(verb),
538 verb2(verb2),
539 view(view),
540 tooltips(tooltips),
541 active(false)
542 {
543 }
545 VerbAction::~VerbAction()
546 {
547 }
549 Gtk::Widget* VerbAction::create_menu_item_vfunc()
550 {
551 // First call in to get the icon rendered if present in SVG
552 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
553 delete widget;
554 widget = 0;
556 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
557 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
558 return widg;
559 }
561 Gtk::Widget* VerbAction::create_tool_item_vfunc()
562 {
563 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
564 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
565 GtkWidget* toolbox = 0;
566 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
567 SP_BUTTON_TYPE_TOGGLE,
568 verb,
569 verb2,
570 view,
571 tooltips );
572 if ( active ) {
573 sp_button_toggle_set_down( SP_BUTTON(button), active);
574 }
575 gtk_widget_show_all( button );
576 Gtk::Widget* wrapped = Glib::wrap(button);
577 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
578 holder->add(*wrapped);
580 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
581 return holder;
582 }
584 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
585 {
586 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
587 Gtk::Action::connect_proxy_vfunc(proxy);
588 }
590 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
591 {
592 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
593 Gtk::Action::disconnect_proxy_vfunc(proxy);
594 }
596 void VerbAction::set_active(bool active)
597 {
598 this->active = active;
599 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
600 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
601 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
602 if (ti) {
603 // *should* have one child that is the SPButton
604 Gtk::Widget* child = ti->get_child();
605 if ( child && SP_IS_BUTTON(child->gobj()) ) {
606 SPButton* button = SP_BUTTON(child->gobj());
607 sp_button_toggle_set_down( button, active );
608 }
609 }
610 }
611 }
613 void VerbAction::on_activate()
614 {
615 if ( verb ) {
616 SPAction *action = verb->get_action(view);
617 if ( action ) {
618 sp_action_perform(action, 0);
619 }
620 }
621 }
623 /* Global text entry widgets necessary for update */
624 /* GtkWidget *dropper_rgb_entry,
625 *dropper_opacity_entry ; */
626 // should be made a private member once this is converted to class
628 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
629 connection->disconnect();
630 delete connection;
631 }
633 static void purge_repr_listener( GObject* obj, GObject* tbl )
634 {
635 (void)obj;
636 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
637 if (oldrepr) { // remove old listener
638 sp_repr_remove_listener_by_data(oldrepr, tbl);
639 Inkscape::GC::release(oldrepr);
640 oldrepr = 0;
641 g_object_set_data( tbl, "repr", NULL );
642 }
643 }
645 GtkWidget *
646 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
647 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
648 Inkscape::UI::View::View *view, GtkTooltips *tt)
649 {
650 SPAction *action = verb->get_action(view);
651 if (!action) return NULL;
653 SPAction *doubleclick_action;
654 if (doubleclick_verb)
655 doubleclick_action = doubleclick_verb->get_action(view);
656 else
657 doubleclick_action = NULL;
659 /* fixme: Handle sensitive/unsensitive */
660 /* fixme: Implement sp_button_new_from_action */
661 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
662 gtk_widget_show(b);
665 unsigned int shortcut = sp_shortcut_get_primary(verb);
666 if (shortcut) {
667 gchar key[256];
668 sp_ui_shortcut_string(shortcut, key);
669 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
670 if ( t ) {
671 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
672 }
673 g_free(tip);
674 } else {
675 if ( t ) {
676 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
677 }
678 }
680 return b;
681 }
684 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
685 {
686 SPAction* targetAction = SP_ACTION(user_data);
687 if ( targetAction ) {
688 sp_action_perform( targetAction, NULL );
689 }
690 }
692 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
693 {
694 if ( data ) {
695 GtkAction* act = GTK_ACTION(data);
696 gtk_action_set_sensitive( act, sensitive );
697 }
698 }
700 static SPActionEventVector action_event_vector = {
701 {NULL},
702 NULL,
703 NULL,
704 sp_action_action_set_sensitive,
705 NULL,
706 NULL
707 };
709 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
710 {
711 GtkAction* act = 0;
713 SPAction* targetAction = verb->get_action(view);
714 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
715 act = GTK_ACTION(inky);
716 gtk_action_set_sensitive( act, targetAction->sensitive );
718 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
720 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
721 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
723 return act;
724 }
726 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
727 {
728 Inkscape::UI::View::View *view = desktop;
729 gint verbsToUse[] = {
730 // disabled until we have icons for them:
731 //find
732 //SP_VERB_EDIT_TILE,
733 //SP_VERB_EDIT_UNTILE,
734 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
735 SP_VERB_DIALOG_DISPLAY,
736 SP_VERB_DIALOG_FILL_STROKE,
737 SP_VERB_DIALOG_NAMEDVIEW,
738 SP_VERB_DIALOG_TEXT,
739 SP_VERB_DIALOG_XML_EDITOR,
740 SP_VERB_EDIT_CLONE,
741 SP_VERB_EDIT_COPY,
742 SP_VERB_EDIT_CUT,
743 SP_VERB_EDIT_DUPLICATE,
744 SP_VERB_EDIT_PASTE,
745 SP_VERB_EDIT_REDO,
746 SP_VERB_EDIT_UNDO,
747 SP_VERB_EDIT_UNLINK_CLONE,
748 SP_VERB_FILE_EXPORT,
749 SP_VERB_FILE_IMPORT,
750 SP_VERB_FILE_NEW,
751 SP_VERB_FILE_OPEN,
752 SP_VERB_FILE_PRINT,
753 SP_VERB_FILE_SAVE,
754 SP_VERB_OBJECT_TO_CURVE,
755 SP_VERB_SELECTION_GROUP,
756 SP_VERB_SELECTION_OUTLINE,
757 SP_VERB_SELECTION_UNGROUP,
758 SP_VERB_ZOOM_1_1,
759 SP_VERB_ZOOM_1_2,
760 SP_VERB_ZOOM_2_1,
761 SP_VERB_ZOOM_DRAWING,
762 SP_VERB_ZOOM_IN,
763 SP_VERB_ZOOM_NEXT,
764 SP_VERB_ZOOM_OUT,
765 SP_VERB_ZOOM_PAGE,
766 SP_VERB_ZOOM_PAGE_WIDTH,
767 SP_VERB_ZOOM_PREV,
768 SP_VERB_ZOOM_SELECTION,
769 };
771 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
773 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
774 Glib::RefPtr<Gtk::ActionGroup> mainActions;
775 if ( groups.find(desktop) != groups.end() ) {
776 mainActions = groups[desktop];
777 }
779 if ( !mainActions ) {
780 mainActions = Gtk::ActionGroup::create("main");
781 groups[desktop] = mainActions;
782 }
784 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
785 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
786 if ( verb ) {
787 if (!mainActions->get_action(verb->get_id())) {
788 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
789 mainActions->add(Glib::wrap(act));
790 }
791 }
792 }
794 if ( !mainActions->get_action("ToolZoom") ) {
795 GtkTooltips *tt = gtk_tooltips_new();
796 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
797 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
798 if ( va ) {
799 mainActions->add(va);
800 if ( i == 0 ) {
801 va->set_active(true);
802 }
803 }
804 }
805 }
808 return mainActions;
809 }
812 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
813 {
814 gtk_widget_set_size_request( widget,
815 widget->allocation.width,
816 widget->allocation.height );
817 }
819 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
820 {
821 gtk_widget_set_size_request( widget, -1, -1 );
822 }
826 GtkWidget *
827 sp_tool_toolbox_new()
828 {
829 GtkTooltips *tt = gtk_tooltips_new();
830 GtkWidget* tb = gtk_toolbar_new();
831 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
832 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
834 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
835 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
837 gtk_widget_set_sensitive(tb, FALSE);
839 GtkWidget *hb = gtk_handle_box_new();
840 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
841 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
842 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
844 gtk_container_add(GTK_CONTAINER(hb), tb);
845 gtk_widget_show(GTK_WIDGET(tb));
847 sigc::connection* conn = new sigc::connection;
848 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
850 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
851 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
853 return hb;
854 }
856 GtkWidget *
857 sp_aux_toolbox_new()
858 {
859 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
861 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
863 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
865 gtk_widget_set_sensitive(tb, FALSE);
867 GtkWidget *hb = gtk_handle_box_new();
868 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
869 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
870 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
872 gtk_container_add(GTK_CONTAINER(hb), tb);
873 gtk_widget_show(GTK_WIDGET(tb));
875 sigc::connection* conn = new sigc::connection;
876 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
878 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
879 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
881 return hb;
882 }
884 //####################################
885 //# Commands Bar
886 //####################################
888 GtkWidget *
889 sp_commands_toolbox_new()
890 {
891 GtkWidget *tb = gtk_toolbar_new();
893 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
894 gtk_widget_set_sensitive(tb, FALSE);
896 GtkWidget *hb = gtk_handle_box_new();
897 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
898 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
899 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
901 gtk_container_add(GTK_CONTAINER(hb), tb);
902 gtk_widget_show(GTK_WIDGET(tb));
904 sigc::connection* conn = new sigc::connection;
905 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
907 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
908 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
910 return hb;
911 }
914 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
915 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
916 gchar const *path, gchar const *data, gdouble def,
917 GtkWidget *focusTarget,
918 GtkWidget *us,
919 GObject *dataKludge,
920 gboolean altx, gchar const *altx_mark,
921 gdouble lower, gdouble upper, gdouble step, gdouble page,
922 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
923 void (*callback)(GtkAdjustment *, GObject *),
924 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
925 {
926 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
927 lower, upper, step, page, page ) );
928 if (us) {
929 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
930 }
932 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
934 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
935 if ( shortLabel ) {
936 g_object_set( act, "short_label", shortLabel, NULL );
937 }
939 if ( (descrCount > 0) && descrLabels && descrValues ) {
940 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
941 }
943 if ( focusTarget ) {
944 ege_adjustment_action_set_focuswidget( act, focusTarget );
945 }
947 if ( altx && altx_mark ) {
948 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
949 }
951 if ( dataKludge ) {
952 g_object_set_data( dataKludge, data, adj );
953 }
955 // Using a cast just to make sure we pass in the right kind of function pointer
956 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
958 return act;
959 }
962 //####################################
963 //# node editing callbacks
964 //####################################
966 /**
967 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
968 */
969 static ShapeEditor *get_current_shape_editor()
970 {
971 if (!SP_ACTIVE_DESKTOP) {
972 return NULL;
973 }
975 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
977 if (!SP_IS_NODE_CONTEXT(event_context)) {
978 return NULL;
979 }
981 return SP_NODE_CONTEXT(event_context)->shape_editor;
982 }
985 void
986 sp_node_path_edit_add(void)
987 {
988 ShapeEditor *shape_editor = get_current_shape_editor();
989 if (shape_editor) shape_editor->add_node();
990 }
992 void
993 sp_node_path_edit_delete(void)
994 {
995 ShapeEditor *shape_editor = get_current_shape_editor();
996 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
997 }
999 void
1000 sp_node_path_edit_delete_segment(void)
1001 {
1002 ShapeEditor *shape_editor = get_current_shape_editor();
1003 if (shape_editor) shape_editor->delete_segment();
1004 }
1006 void
1007 sp_node_path_edit_break(void)
1008 {
1009 ShapeEditor *shape_editor = get_current_shape_editor();
1010 if (shape_editor) shape_editor->break_at_nodes();
1011 }
1013 void
1014 sp_node_path_edit_join(void)
1015 {
1016 ShapeEditor *shape_editor = get_current_shape_editor();
1017 if (shape_editor) shape_editor->join_nodes();
1018 }
1020 void
1021 sp_node_path_edit_join_segment(void)
1022 {
1023 ShapeEditor *shape_editor = get_current_shape_editor();
1024 if (shape_editor) shape_editor->join_segments();
1025 }
1027 void
1028 sp_node_path_edit_toline(void)
1029 {
1030 ShapeEditor *shape_editor = get_current_shape_editor();
1031 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1032 }
1034 void
1035 sp_node_path_edit_tocurve(void)
1036 {
1037 ShapeEditor *shape_editor = get_current_shape_editor();
1038 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1039 }
1041 void
1042 sp_node_path_edit_cusp(void)
1043 {
1044 ShapeEditor *shape_editor = get_current_shape_editor();
1045 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1046 }
1048 void
1049 sp_node_path_edit_smooth(void)
1050 {
1051 ShapeEditor *shape_editor = get_current_shape_editor();
1052 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1053 }
1055 void
1056 sp_node_path_edit_symmetrical(void)
1057 {
1058 ShapeEditor *shape_editor = get_current_shape_editor();
1059 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1060 }
1062 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1063 bool show = gtk_toggle_action_get_active( act );
1064 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1065 ShapeEditor *shape_editor = get_current_shape_editor();
1066 if (shape_editor) shape_editor->show_handles(show);
1067 }
1069 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1070 bool show = gtk_toggle_action_get_active( act );
1071 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1072 ShapeEditor *shape_editor = get_current_shape_editor();
1073 if (shape_editor) shape_editor->show_helperpath(show);
1074 }
1076 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1077 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1078 }
1080 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1081 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1082 }
1084 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1085 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1086 }
1088 /* is called when the node selection is modified */
1089 static void
1090 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1091 {
1092 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1093 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1094 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1095 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1097 // quit if run by the attr_changed listener
1098 if (g_object_get_data( tbl, "freeze" )) {
1099 return;
1100 }
1102 // in turn, prevent listener from responding
1103 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1105 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1106 SPUnit const *unit = tracker->getActiveUnit();
1108 ShapeEditor *shape_editor = get_current_shape_editor();
1109 if (shape_editor && shape_editor->has_nodepath()) {
1110 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1111 int n_selected = 0;
1112 if (nodepath) {
1113 n_selected = nodepath->numSelected();
1114 }
1116 if (n_selected == 0) {
1117 gtk_action_set_sensitive(xact, FALSE);
1118 gtk_action_set_sensitive(yact, FALSE);
1119 } else {
1120 gtk_action_set_sensitive(xact, TRUE);
1121 gtk_action_set_sensitive(yact, TRUE);
1122 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1123 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1125 if (n_selected == 1) {
1126 Geom::Point sel_node = nodepath->singleSelectedCoords();
1127 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1128 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1129 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1130 }
1131 } else {
1132 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1133 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1134 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1135 /* Note: Currently x and y will always have a value, even if the coordinates of the
1136 selected nodes don't coincide (in this case we use the coordinates of the center
1137 of the bounding box). So the entries are never set to zero. */
1138 // FIXME: Maybe we should clear the entry if several nodes are selected
1139 // instead of providing a kind of average value
1140 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1141 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1142 }
1143 }
1144 }
1145 } else {
1146 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1147 gtk_action_set_sensitive(xact, FALSE);
1148 gtk_action_set_sensitive(yact, FALSE);
1149 }
1151 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1152 }
1154 static void
1155 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1156 {
1157 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1159 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1160 SPUnit const *unit = tracker->getActiveUnit();
1162 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1163 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1164 }
1166 // quit if run by the attr_changed listener
1167 if (g_object_get_data( tbl, "freeze" )) {
1168 return;
1169 }
1171 // in turn, prevent listener from responding
1172 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1174 ShapeEditor *shape_editor = get_current_shape_editor();
1175 if (shape_editor && shape_editor->has_nodepath()) {
1176 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1177 if (!strcmp(value_name, "x")) {
1178 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1179 }
1180 if (!strcmp(value_name, "y")) {
1181 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1182 }
1183 }
1185 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1186 }
1188 static void
1189 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1190 {
1191 sp_node_path_value_changed(adj, tbl, "x");
1192 }
1194 static void
1195 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1196 {
1197 sp_node_path_value_changed(adj, tbl, "y");
1198 }
1200 void
1201 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1202 {
1203 {
1204 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1205 SPItem *item = selection->singleItem();
1206 if (item && SP_IS_LPE_ITEM(item)) {
1207 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1208 gtk_action_set_sensitive(w, TRUE);
1209 } else {
1210 gtk_action_set_sensitive(w, FALSE);
1211 }
1212 } else {
1213 gtk_action_set_sensitive(w, FALSE);
1214 }
1215 }
1217 {
1218 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1219 SPItem *item = selection->singleItem();
1220 if (item && item->clip_ref && item->clip_ref->getObject()) {
1221 gtk_action_set_sensitive(w, TRUE);
1222 } else {
1223 gtk_action_set_sensitive(w, FALSE);
1224 }
1225 }
1227 {
1228 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1229 SPItem *item = selection->singleItem();
1230 if (item && item->mask_ref && item->mask_ref->getObject()) {
1231 gtk_action_set_sensitive(w, TRUE);
1232 } else {
1233 gtk_action_set_sensitive(w, FALSE);
1234 }
1235 }
1236 }
1238 void
1239 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1240 {
1241 sp_node_toolbox_sel_changed (selection, tbl);
1242 }
1246 //################################
1247 //## Node Editing Toolbox ##
1248 //################################
1250 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1251 {
1252 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1253 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1254 g_object_set_data( holder, "tracker", tracker );
1256 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1258 {
1259 InkAction* inky = ink_action_new( "NodeInsertAction",
1260 _("Insert node"),
1261 _("Insert new nodes into selected segments"),
1262 "node_insert",
1263 secondarySize );
1264 g_object_set( inky, "short_label", _("Insert"), NULL );
1265 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1266 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1267 }
1269 {
1270 InkAction* inky = ink_action_new( "NodeDeleteAction",
1271 _("Delete node"),
1272 _("Delete selected nodes"),
1273 "node_delete",
1274 secondarySize );
1275 g_object_set( inky, "short_label", _("Delete"), NULL );
1276 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1277 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1278 }
1280 {
1281 InkAction* inky = ink_action_new( "NodeJoinAction",
1282 _("Join endnodes"),
1283 _("Join selected endnodes"),
1284 "node_join",
1285 secondarySize );
1286 g_object_set( inky, "short_label", _("Join"), NULL );
1287 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1288 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1289 }
1291 {
1292 InkAction* inky = ink_action_new( "NodeBreakAction",
1293 _("Break nodes"),
1294 _("Break path at selected nodes"),
1295 "node_break",
1296 secondarySize );
1297 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1298 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299 }
1302 {
1303 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1304 _("Join with segment"),
1305 _("Join selected endnodes with a new segment"),
1306 "node_join_segment",
1307 secondarySize );
1308 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1309 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1310 }
1312 {
1313 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1314 _("Delete segment"),
1315 _("Delete segment between two non-endpoint nodes"),
1316 "node_delete_segment",
1317 secondarySize );
1318 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1319 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1320 }
1322 {
1323 InkAction* inky = ink_action_new( "NodeCuspAction",
1324 _("Node Cusp"),
1325 _("Make selected nodes corner"),
1326 "node_cusp",
1327 secondarySize );
1328 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1329 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1330 }
1332 {
1333 InkAction* inky = ink_action_new( "NodeSmoothAction",
1334 _("Node Smooth"),
1335 _("Make selected nodes smooth"),
1336 "node_smooth",
1337 secondarySize );
1338 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1339 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1340 }
1342 {
1343 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1344 _("Node Symmetric"),
1345 _("Make selected nodes symmetric"),
1346 "node_symmetric",
1347 secondarySize );
1348 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1349 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1350 }
1352 {
1353 InkAction* inky = ink_action_new( "NodeLineAction",
1354 _("Node Line"),
1355 _("Make selected segments lines"),
1356 "node_line",
1357 secondarySize );
1358 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1359 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1360 }
1362 {
1363 InkAction* inky = ink_action_new( "NodeCurveAction",
1364 _("Node Curve"),
1365 _("Make selected segments curves"),
1366 "node_curve",
1367 secondarySize );
1368 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1369 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1370 }
1372 {
1373 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1374 _("Show Handles"),
1375 _("Show the Bezier handles of selected nodes"),
1376 "nodes_show_handles",
1377 Inkscape::ICON_SIZE_DECORATION );
1378 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1379 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1380 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1381 }
1383 {
1384 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1385 _("Show Outline"),
1386 _("Show the outline of the path"),
1387 "nodes_show_helperpath",
1388 Inkscape::ICON_SIZE_DECORATION );
1389 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1390 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1391 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1392 }
1394 {
1395 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1396 _("Next path effect parameter"),
1397 _("Show next path effect parameter for editing"),
1398 "edit_next_parameter",
1399 Inkscape::ICON_SIZE_DECORATION );
1400 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1401 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402 g_object_set_data( holder, "nodes_lpeedit", inky);
1403 }
1405 {
1406 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1407 _("Edit clipping path"),
1408 _("Edit the clipping path of the object"),
1409 "nodeedit-clippath",
1410 Inkscape::ICON_SIZE_DECORATION );
1411 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1412 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1413 g_object_set_data( holder, "nodes_clippathedit", inky);
1414 }
1416 {
1417 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1418 _("Edit mask path"),
1419 _("Edit the mask of the object"),
1420 "nodeedit-mask",
1421 Inkscape::ICON_SIZE_DECORATION );
1422 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1423 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424 g_object_set_data( holder, "nodes_maskedit", inky);
1425 }
1427 /* X coord of selected node(s) */
1428 {
1429 EgeAdjustmentAction* eact = 0;
1430 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1431 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1432 eact = create_adjustment_action( "NodeXAction",
1433 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1434 "tools.nodes", "Xcoord", 0,
1435 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1436 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1437 labels, values, G_N_ELEMENTS(labels),
1438 sp_node_path_x_value_changed );
1439 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1440 g_object_set_data( holder, "nodes_x_action", eact );
1441 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1442 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1443 }
1445 /* Y coord of selected node(s) */
1446 {
1447 EgeAdjustmentAction* eact = 0;
1448 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1449 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1450 eact = create_adjustment_action( "NodeYAction",
1451 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1452 "tools.nodes", "Ycoord", 0,
1453 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1454 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1455 labels, values, G_N_ELEMENTS(labels),
1456 sp_node_path_y_value_changed );
1457 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1458 g_object_set_data( holder, "nodes_y_action", eact );
1459 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1460 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1461 }
1463 // add the units menu
1464 {
1465 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1466 gtk_action_group_add_action( mainActions, act );
1467 }
1470 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1472 //watch selection
1473 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1475 sigc::connection *c_selection_changed =
1476 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1477 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1478 pool->add_connection ("selection-changed", c_selection_changed);
1480 sigc::connection *c_selection_modified =
1481 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1482 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1483 pool->add_connection ("selection-modified", c_selection_modified);
1485 sigc::connection *c_subselection_changed =
1486 new sigc::connection (desktop->connectToolSubselectionChanged
1487 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1488 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1490 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1492 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1493 } // end of sp_node_toolbox_prep()
1496 //########################
1497 //## Zoom Toolbox ##
1498 //########################
1500 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1501 {
1502 // no custom GtkAction setup needed
1503 } // end of sp_zoom_toolbox_prep()
1505 void
1506 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1507 {
1508 toolbox_set_desktop(toolbox,
1509 desktop,
1510 setup_tool_toolbox,
1511 update_tool_toolbox,
1512 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1513 "event_context_connection")));
1514 }
1517 void
1518 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1519 {
1520 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1521 desktop,
1522 setup_aux_toolbox,
1523 update_aux_toolbox,
1524 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1525 "event_context_connection")));
1526 }
1528 void
1529 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1530 {
1531 toolbox_set_desktop(toolbox,
1532 desktop,
1533 setup_commands_toolbox,
1534 update_commands_toolbox,
1535 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1536 "event_context_connection")));
1537 }
1539 static void
1540 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1541 {
1542 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1543 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1545 if (old_desktop) {
1546 GList *children, *iter;
1548 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1549 for ( iter = children ; iter ; iter = iter->next ) {
1550 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1551 }
1552 g_list_free(children);
1553 }
1555 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1557 if (desktop) {
1558 gtk_widget_set_sensitive(toolbox, TRUE);
1559 setup_func(toolbox, desktop);
1560 update_func(desktop, desktop->event_context, toolbox);
1561 *conn = desktop->connectEventContextChanged
1562 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1563 } else {
1564 gtk_widget_set_sensitive(toolbox, FALSE);
1565 }
1567 } // end of toolbox_set_desktop()
1570 static void
1571 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1572 {
1573 gchar const * descr =
1574 "<ui>"
1575 " <toolbar name='ToolToolbar'>"
1576 " <toolitem action='ToolSelector' />"
1577 " <toolitem action='ToolNode' />"
1578 " <toolitem action='ToolTweak' />"
1579 " <toolitem action='ToolZoom' />"
1580 " <toolitem action='ToolRect' />"
1581 " <toolitem action='Tool3DBox' />"
1582 " <toolitem action='ToolArc' />"
1583 " <toolitem action='ToolStar' />"
1584 " <toolitem action='ToolSpiral' />"
1585 " <toolitem action='ToolPencil' />"
1586 " <toolitem action='ToolPen' />"
1587 " <toolitem action='ToolCalligraphic' />"
1588 " <toolitem action='ToolEraser' />"
1589 // " <toolitem action='ToolLPETool' />"
1590 " <toolitem action='ToolPaintBucket' />"
1591 " <toolitem action='ToolText' />"
1592 " <toolitem action='ToolConnector' />"
1593 " <toolitem action='ToolGradient' />"
1594 " <toolitem action='ToolDropper' />"
1595 " </toolbar>"
1596 "</ui>";
1597 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1598 GtkUIManager* mgr = gtk_ui_manager_new();
1599 GError* errVal = 0;
1601 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1602 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1604 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1605 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1606 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1607 }
1608 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1609 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1611 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1612 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1614 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1616 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1617 if ( child ) {
1618 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1619 }
1621 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1622 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1623 }
1626 static void
1627 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1628 {
1629 gchar const *const tname = ( eventcontext
1630 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1631 : NULL );
1632 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1634 for (int i = 0 ; tools[i].type_name ; i++ ) {
1635 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1636 if ( act ) {
1637 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1638 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1639 if ( verbAct ) {
1640 verbAct->set_active(setActive);
1641 }
1642 }
1643 }
1644 }
1646 static void
1647 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1648 {
1649 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1650 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1651 GtkUIManager* mgr = gtk_ui_manager_new();
1652 GError* errVal = 0;
1653 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1654 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1656 std::map<std::string, GtkWidget*> dataHolders;
1658 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1659 if ( aux_toolboxes[i].prep_func ) {
1660 // converted to GtkActions and UIManager
1662 GtkWidget* kludge = gtk_toolbar_new();
1663 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1664 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1665 dataHolders[aux_toolboxes[i].type_name] = kludge;
1666 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1667 } else {
1669 GtkWidget *sub_toolbox = 0;
1670 if (aux_toolboxes[i].create_func == NULL)
1671 sub_toolbox = sp_empty_toolbox_new(desktop);
1672 else {
1673 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1674 }
1676 gtk_size_group_add_widget( grouper, sub_toolbox );
1678 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1679 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1681 }
1682 }
1684 // Second pass to create toolbars *after* all GtkActions are created
1685 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1686 if ( aux_toolboxes[i].prep_func ) {
1687 // converted to GtkActions and UIManager
1689 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1691 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1692 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1694 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1695 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1696 g_free( tmp );
1697 tmp = 0;
1699 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1700 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1701 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1702 }
1703 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1706 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1708 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1709 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1710 swatch->setDesktop( desktop );
1711 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1712 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1713 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1714 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 );
1715 }
1717 gtk_widget_show_all( holder );
1718 sp_set_font_size_smaller( holder );
1720 gtk_size_group_add_widget( grouper, holder );
1722 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1723 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1724 }
1725 }
1727 g_object_unref( G_OBJECT(grouper) );
1728 }
1730 static void
1731 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1732 {
1733 gchar const *tname = ( eventcontext
1734 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1735 : NULL );
1736 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1737 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1738 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1739 gtk_widget_show_all(sub_toolbox);
1740 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1741 } else {
1742 gtk_widget_hide(sub_toolbox);
1743 }
1744 }
1745 }
1747 static void
1748 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1749 {
1750 gchar const * descr =
1751 "<ui>"
1752 " <toolbar name='CommandsToolbar'>"
1753 " <toolitem action='FileNew' />"
1754 " <toolitem action='FileOpen' />"
1755 " <toolitem action='FileSave' />"
1756 " <toolitem action='FilePrint' />"
1757 " <separator />"
1758 " <toolitem action='FileImport' />"
1759 " <toolitem action='FileExport' />"
1760 " <separator />"
1761 " <toolitem action='EditUndo' />"
1762 " <toolitem action='EditRedo' />"
1763 " <separator />"
1764 " <toolitem action='EditCopy' />"
1765 " <toolitem action='EditCut' />"
1766 " <toolitem action='EditPaste' />"
1767 " <separator />"
1768 " <toolitem action='ZoomSelection' />"
1769 " <toolitem action='ZoomDrawing' />"
1770 " <toolitem action='ZoomPage' />"
1771 " <separator />"
1772 " <toolitem action='EditDuplicate' />"
1773 " <toolitem action='EditClone' />"
1774 " <toolitem action='EditUnlinkClone' />"
1775 " <separator />"
1776 " <toolitem action='SelectionGroup' />"
1777 " <toolitem action='SelectionUnGroup' />"
1778 " <separator />"
1779 " <toolitem action='DialogFillStroke' />"
1780 " <toolitem action='DialogText' />"
1781 " <toolitem action='DialogXMLEditor' />"
1782 " <toolitem action='DialogAlignDistribute' />"
1783 " <separator />"
1784 " <toolitem action='DialogPreferences' />"
1785 " <toolitem action='DialogDocumentProperties' />"
1786 " </toolbar>"
1787 "</ui>";
1788 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1791 GtkUIManager* mgr = gtk_ui_manager_new();
1792 GError* errVal = 0;
1794 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1795 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1797 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1798 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1799 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1800 }
1802 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1803 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1805 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1806 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1809 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1811 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1812 if ( child ) {
1813 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1814 }
1816 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1817 }
1819 static void
1820 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1821 {
1822 }
1824 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1825 {
1826 gtk_widget_show(toolbox_toplevel);
1827 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1829 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1830 if (!shown_toolbox) {
1831 return;
1832 }
1833 gtk_widget_show(toolbox);
1835 gtk_widget_show_all(shown_toolbox);
1836 }
1838 static GtkWidget *
1839 sp_empty_toolbox_new(SPDesktop *desktop)
1840 {
1841 GtkWidget *tbl = gtk_toolbar_new();
1842 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1843 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1845 gtk_widget_show_all(tbl);
1846 sp_set_font_size_smaller (tbl);
1848 return tbl;
1849 }
1851 #define MODE_LABEL_WIDTH 70
1853 //########################
1854 //## Star ##
1855 //########################
1857 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1858 {
1859 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1861 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1862 // do not remember prefs if this call is initiated by an undo change, because undoing object
1863 // creation sets bogus values to its attributes before it is deleted
1864 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1865 }
1867 // quit if run by the attr_changed listener
1868 if (g_object_get_data( dataKludge, "freeze" )) {
1869 return;
1870 }
1872 // in turn, prevent listener from responding
1873 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1875 bool modmade = false;
1877 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1878 GSList const *items = selection->itemList();
1879 for (; items != NULL; items = items->next) {
1880 if (SP_IS_STAR((SPItem *) items->data)) {
1881 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1882 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1883 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1884 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1885 + M_PI / (gint)adj->value));
1886 SP_OBJECT((SPItem *) items->data)->updateRepr();
1887 modmade = true;
1888 }
1889 }
1890 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1891 _("Star: Change number of corners"));
1893 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1894 }
1896 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1897 {
1898 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1900 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1901 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1902 }
1904 // quit if run by the attr_changed listener
1905 if (g_object_get_data( dataKludge, "freeze" )) {
1906 return;
1907 }
1909 // in turn, prevent listener from responding
1910 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1912 bool modmade = false;
1913 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1914 GSList const *items = selection->itemList();
1915 for (; items != NULL; items = items->next) {
1916 if (SP_IS_STAR((SPItem *) items->data)) {
1917 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1919 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1920 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1921 if (r2 < r1) {
1922 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1923 } else {
1924 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1925 }
1927 SP_OBJECT((SPItem *) items->data)->updateRepr();
1928 modmade = true;
1929 }
1930 }
1932 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1933 _("Star: Change spoke ratio"));
1935 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1936 }
1938 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1939 {
1940 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1941 bool flat = ege_select_one_action_get_active( act ) == 0;
1943 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1944 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1945 flat ? "true" : "false" );
1946 }
1948 // quit if run by the attr_changed listener
1949 if (g_object_get_data( dataKludge, "freeze" )) {
1950 return;
1951 }
1953 // in turn, prevent listener from responding
1954 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1956 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1957 GSList const *items = selection->itemList();
1958 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1959 bool modmade = false;
1961 if ( prop_action ) {
1962 gtk_action_set_sensitive( prop_action, !flat );
1963 }
1965 for (; items != NULL; items = items->next) {
1966 if (SP_IS_STAR((SPItem *) items->data)) {
1967 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1968 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1969 SP_OBJECT((SPItem *) items->data)->updateRepr();
1970 modmade = true;
1971 }
1972 }
1974 if (modmade) {
1975 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1976 flat ? _("Make polygon") : _("Make star"));
1977 }
1979 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1980 }
1982 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1983 {
1984 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1986 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1987 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1988 }
1990 // quit if run by the attr_changed listener
1991 if (g_object_get_data( dataKludge, "freeze" )) {
1992 return;
1993 }
1995 // in turn, prevent listener from responding
1996 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1998 bool modmade = false;
2000 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2001 GSList const *items = selection->itemList();
2002 for (; items != NULL; items = items->next) {
2003 if (SP_IS_STAR((SPItem *) items->data)) {
2004 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2005 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2006 SP_OBJECT(items->data)->updateRepr();
2007 modmade = true;
2008 }
2009 }
2010 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2011 _("Star: Change rounding"));
2013 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2014 }
2016 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2017 {
2018 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2020 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2021 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2022 }
2024 // quit if run by the attr_changed listener
2025 if (g_object_get_data( dataKludge, "freeze" )) {
2026 return;
2027 }
2029 // in turn, prevent listener from responding
2030 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2032 bool modmade = false;
2034 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2035 GSList const *items = selection->itemList();
2036 for (; items != NULL; items = items->next) {
2037 if (SP_IS_STAR((SPItem *) items->data)) {
2038 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2039 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2040 SP_OBJECT(items->data)->updateRepr();
2041 modmade = true;
2042 }
2043 }
2044 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2045 _("Star: Change randomization"));
2047 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2048 }
2051 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2052 gchar const */*old_value*/, gchar const */*new_value*/,
2053 bool /*is_interactive*/, gpointer data)
2054 {
2055 GtkWidget *tbl = GTK_WIDGET(data);
2057 // quit if run by the _changed callbacks
2058 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2059 return;
2060 }
2062 // in turn, prevent callbacks from responding
2063 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2065 GtkAdjustment *adj = 0;
2067 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2068 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2070 if (!strcmp(name, "inkscape:randomized")) {
2071 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2072 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2073 } else if (!strcmp(name, "inkscape:rounded")) {
2074 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2075 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2076 } else if (!strcmp(name, "inkscape:flatsided")) {
2077 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2078 char const *flatsides = repr->attribute("inkscape:flatsided");
2079 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2080 if ( flatsides && !strcmp(flatsides,"false") ) {
2081 ege_select_one_action_set_active( flat_action, 1 );
2082 gtk_action_set_sensitive( prop_action, TRUE );
2083 } else {
2084 ege_select_one_action_set_active( flat_action, 0 );
2085 gtk_action_set_sensitive( prop_action, FALSE );
2086 }
2087 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2088 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2089 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2090 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2091 if (r2 < r1) {
2092 gtk_adjustment_set_value(adj, r2/r1);
2093 } else {
2094 gtk_adjustment_set_value(adj, r1/r2);
2095 }
2096 } else if (!strcmp(name, "sodipodi:sides")) {
2097 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2098 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2099 }
2101 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2102 }
2105 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2106 {
2107 NULL, /* child_added */
2108 NULL, /* child_removed */
2109 star_tb_event_attr_changed,
2110 NULL, /* content_changed */
2111 NULL /* order_changed */
2112 };
2115 /**
2116 * \param selection Should not be NULL.
2117 */
2118 static void
2119 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2120 {
2121 int n_selected = 0;
2122 Inkscape::XML::Node *repr = NULL;
2124 purge_repr_listener( tbl, tbl );
2126 for (GSList const *items = selection->itemList();
2127 items != NULL;
2128 items = items->next)
2129 {
2130 if (SP_IS_STAR((SPItem *) items->data)) {
2131 n_selected++;
2132 repr = SP_OBJECT_REPR((SPItem *) items->data);
2133 }
2134 }
2136 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2138 if (n_selected == 0) {
2139 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2140 } else if (n_selected == 1) {
2141 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2143 if (repr) {
2144 g_object_set_data( tbl, "repr", repr );
2145 Inkscape::GC::anchor(repr);
2146 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2147 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2148 }
2149 } else {
2150 // FIXME: implement averaging of all parameters for multiple selected stars
2151 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2152 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2153 }
2154 }
2157 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2158 {
2159 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2160 // callbacks to lump all the changes for all selected objects in one undo step
2162 GtkAdjustment *adj = 0;
2164 // fixme: make settable in prefs!
2165 gint mag = 5;
2166 gdouble prop = 0.5;
2167 gboolean flat = FALSE;
2168 gdouble randomized = 0;
2169 gdouble rounded = 0;
2171 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2172 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2174 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2175 gtk_action_set_sensitive( sb2, !flat );
2177 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2178 gtk_adjustment_set_value(adj, mag);
2179 gtk_adjustment_value_changed(adj);
2181 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2182 gtk_adjustment_set_value(adj, prop);
2183 gtk_adjustment_value_changed(adj);
2185 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2186 gtk_adjustment_set_value(adj, rounded);
2187 gtk_adjustment_value_changed(adj);
2189 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2190 gtk_adjustment_set_value(adj, randomized);
2191 gtk_adjustment_value_changed(adj);
2192 }
2195 void
2196 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2197 {
2198 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2199 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2200 GtkWidget *l = gtk_label_new(NULL);
2201 gtk_label_set_markup(GTK_LABEL(l), title);
2202 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2203 if ( GTK_IS_TOOLBAR(tbl) ) {
2204 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2205 } else {
2206 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2207 }
2208 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2209 }
2212 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2213 {
2214 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2216 {
2217 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2218 ege_output_action_set_use_markup( act, TRUE );
2219 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2220 g_object_set_data( holder, "mode_action", act );
2221 }
2223 {
2224 EgeAdjustmentAction* eact = 0;
2225 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2226 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2228 /* Flatsided checkbox */
2229 {
2230 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2232 GtkTreeIter iter;
2233 gtk_list_store_append( model, &iter );
2234 gtk_list_store_set( model, &iter,
2235 0, _("Polygon"),
2236 1, _("Regular polygon (with one handle) instead of a star"),
2237 2, "star_flat",
2238 -1 );
2240 gtk_list_store_append( model, &iter );
2241 gtk_list_store_set( model, &iter,
2242 0, _("Star"),
2243 1, _("Star instead of a regular polygon (with one handle)"),
2244 2, "star_angled",
2245 -1 );
2247 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2248 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2249 g_object_set_data( holder, "flat_action", act );
2251 ege_select_one_action_set_appearance( act, "full" );
2252 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2253 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2254 ege_select_one_action_set_icon_column( act, 2 );
2255 ege_select_one_action_set_icon_size( act, secondarySize );
2256 ege_select_one_action_set_tooltip_column( act, 1 );
2258 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2259 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2260 }
2262 /* Magnitude */
2263 {
2264 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2265 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2266 eact = create_adjustment_action( "MagnitudeAction",
2267 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2268 "tools.shapes.star", "magnitude", 3,
2269 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2270 3, 1024, 1, 5,
2271 labels, values, G_N_ELEMENTS(labels),
2272 sp_stb_magnitude_value_changed,
2273 1.0, 0 );
2274 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2275 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2276 }
2278 /* Spoke ratio */
2279 {
2280 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2281 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2282 eact = create_adjustment_action( "SpokeAction",
2283 _("Spoke ratio"), _("Spoke ratio:"),
2284 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2285 // Base radius is the same for the closest handle.
2286 _("Base radius to tip radius ratio"),
2287 "tools.shapes.star", "proportion", 0.5,
2288 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2289 0.01, 1.0, 0.01, 0.1,
2290 labels, values, G_N_ELEMENTS(labels),
2291 sp_stb_proportion_value_changed );
2292 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2293 g_object_set_data( holder, "prop_action", eact );
2294 }
2296 if ( !isFlatSided ) {
2297 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2298 } else {
2299 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2300 }
2302 /* Roundedness */
2303 {
2304 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2305 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2306 eact = create_adjustment_action( "RoundednessAction",
2307 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2308 "tools.shapes.star", "rounded", 0.0,
2309 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2310 -10.0, 10.0, 0.01, 0.1,
2311 labels, values, G_N_ELEMENTS(labels),
2312 sp_stb_rounded_value_changed );
2313 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2314 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2315 }
2317 /* Randomization */
2318 {
2319 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2320 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2321 eact = create_adjustment_action( "RandomizationAction",
2322 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2323 "tools.shapes.star", "randomized", 0.0,
2324 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2325 -10.0, 10.0, 0.001, 0.01,
2326 labels, values, G_N_ELEMENTS(labels),
2327 sp_stb_randomized_value_changed, 0.1, 3 );
2328 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2329 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2330 }
2331 }
2333 {
2334 /* Reset */
2335 {
2336 GtkAction* act = gtk_action_new( "StarResetAction",
2337 _("Defaults"),
2338 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2339 GTK_STOCK_CLEAR );
2340 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2341 gtk_action_group_add_action( mainActions, act );
2342 gtk_action_set_sensitive( act, TRUE );
2343 }
2344 }
2346 sigc::connection *connection = new sigc::connection(
2347 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2348 );
2349 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2350 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2351 }
2354 //########################
2355 //## Rect ##
2356 //########################
2358 static void sp_rtb_sensitivize( GObject *tbl )
2359 {
2360 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2361 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2362 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2364 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2365 gtk_action_set_sensitive( not_rounded, FALSE );
2366 } else {
2367 gtk_action_set_sensitive( not_rounded, TRUE );
2368 }
2369 }
2372 static void
2373 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2374 void (*setter)(SPRect *, gdouble))
2375 {
2376 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2378 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2379 SPUnit const *unit = tracker->getActiveUnit();
2381 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2382 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2383 }
2385 // quit if run by the attr_changed listener
2386 if (g_object_get_data( tbl, "freeze" )) {
2387 return;
2388 }
2390 // in turn, prevent listener from responding
2391 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2393 bool modmade = false;
2394 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2395 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2396 if (SP_IS_RECT(items->data)) {
2397 if (adj->value != 0) {
2398 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2399 } else {
2400 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2401 }
2402 modmade = true;
2403 }
2404 }
2406 sp_rtb_sensitivize( tbl );
2408 if (modmade) {
2409 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2410 _("Change rectangle"));
2411 }
2413 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2414 }
2416 static void
2417 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2418 {
2419 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2420 }
2422 static void
2423 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2424 {
2425 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2426 }
2428 static void
2429 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2430 {
2431 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2432 }
2434 static void
2435 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2436 {
2437 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2438 }
2442 static void
2443 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2444 {
2445 GtkAdjustment *adj = 0;
2447 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2448 gtk_adjustment_set_value(adj, 0.0);
2449 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2450 gtk_adjustment_value_changed(adj);
2452 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2453 gtk_adjustment_set_value(adj, 0.0);
2454 gtk_adjustment_value_changed(adj);
2456 sp_rtb_sensitivize( obj );
2457 }
2459 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2460 gchar const */*old_value*/, gchar const */*new_value*/,
2461 bool /*is_interactive*/, gpointer data)
2462 {
2463 GObject *tbl = G_OBJECT(data);
2465 // quit if run by the _changed callbacks
2466 if (g_object_get_data( tbl, "freeze" )) {
2467 return;
2468 }
2470 // in turn, prevent callbacks from responding
2471 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2473 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2474 SPUnit const *unit = tracker->getActiveUnit();
2476 gpointer item = g_object_get_data( tbl, "item" );
2477 if (item && SP_IS_RECT(item)) {
2478 {
2479 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2480 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2481 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2482 }
2484 {
2485 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2486 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2487 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2488 }
2490 {
2491 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2492 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2493 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2494 }
2496 {
2497 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2498 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2499 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2500 }
2501 }
2503 sp_rtb_sensitivize( tbl );
2505 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2506 }
2509 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2510 NULL, /* child_added */
2511 NULL, /* child_removed */
2512 rect_tb_event_attr_changed,
2513 NULL, /* content_changed */
2514 NULL /* order_changed */
2515 };
2517 /**
2518 * \param selection should not be NULL.
2519 */
2520 static void
2521 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2522 {
2523 int n_selected = 0;
2524 Inkscape::XML::Node *repr = NULL;
2525 SPItem *item = NULL;
2527 if ( g_object_get_data( tbl, "repr" ) ) {
2528 g_object_set_data( tbl, "item", NULL );
2529 }
2530 purge_repr_listener( tbl, tbl );
2532 for (GSList const *items = selection->itemList();
2533 items != NULL;
2534 items = items->next) {
2535 if (SP_IS_RECT((SPItem *) items->data)) {
2536 n_selected++;
2537 item = (SPItem *) items->data;
2538 repr = SP_OBJECT_REPR(item);
2539 }
2540 }
2542 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2544 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2546 if (n_selected == 0) {
2547 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2549 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2550 gtk_action_set_sensitive(w, FALSE);
2551 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2552 gtk_action_set_sensitive(h, FALSE);
2554 } else if (n_selected == 1) {
2555 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2556 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2558 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2559 gtk_action_set_sensitive(w, TRUE);
2560 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2561 gtk_action_set_sensitive(h, TRUE);
2563 if (repr) {
2564 g_object_set_data( tbl, "repr", repr );
2565 g_object_set_data( tbl, "item", item );
2566 Inkscape::GC::anchor(repr);
2567 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2568 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2569 }
2570 } else {
2571 // FIXME: implement averaging of all parameters for multiple selected
2572 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2573 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2574 sp_rtb_sensitivize( tbl );
2575 }
2576 }
2579 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2580 {
2581 EgeAdjustmentAction* eact = 0;
2582 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2584 {
2585 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2586 ege_output_action_set_use_markup( act, TRUE );
2587 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2588 g_object_set_data( holder, "mode_action", act );
2589 }
2591 // rx/ry units menu: create
2592 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2593 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2594 // fixme: add % meaning per cent of the width/height
2595 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2596 g_object_set_data( holder, "tracker", tracker );
2598 /* W */
2599 {
2600 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2601 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2602 eact = create_adjustment_action( "RectWidthAction",
2603 _("Width"), _("W:"), _("Width of rectangle"),
2604 "tools.shapes.rect", "width", 0,
2605 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2606 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2607 labels, values, G_N_ELEMENTS(labels),
2608 sp_rtb_width_value_changed );
2609 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2610 g_object_set_data( holder, "width_action", eact );
2611 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2612 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2613 }
2615 /* H */
2616 {
2617 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2618 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2619 eact = create_adjustment_action( "RectHeightAction",
2620 _("Height"), _("H:"), _("Height of rectangle"),
2621 "tools.shapes.rect", "height", 0,
2622 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2623 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2624 labels, values, G_N_ELEMENTS(labels),
2625 sp_rtb_height_value_changed );
2626 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2627 g_object_set_data( holder, "height_action", eact );
2628 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2629 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2630 }
2632 /* rx */
2633 {
2634 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2635 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2636 eact = create_adjustment_action( "RadiusXAction",
2637 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2638 "tools.shapes.rect", "rx", 0,
2639 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2640 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2641 labels, values, G_N_ELEMENTS(labels),
2642 sp_rtb_rx_value_changed);
2643 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2644 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2645 }
2647 /* ry */
2648 {
2649 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2650 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2651 eact = create_adjustment_action( "RadiusYAction",
2652 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2653 "tools.shapes.rect", "ry", 0,
2654 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2655 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2656 labels, values, G_N_ELEMENTS(labels),
2657 sp_rtb_ry_value_changed);
2658 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2659 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2660 }
2662 // add the units menu
2663 {
2664 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2665 gtk_action_group_add_action( mainActions, act );
2666 }
2668 /* Reset */
2669 {
2670 InkAction* inky = ink_action_new( "RectResetAction",
2671 _("Not rounded"),
2672 _("Make corners sharp"),
2673 "squared_corner",
2674 secondarySize );
2675 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2676 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2677 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2678 g_object_set_data( holder, "not_rounded", inky );
2679 }
2681 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2682 sp_rtb_sensitivize( holder );
2684 sigc::connection *connection = new sigc::connection(
2685 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2686 );
2687 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2688 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2689 }
2691 //########################
2692 //## 3D Box ##
2693 //########################
2695 // normalize angle so that it lies in the interval [0,360]
2696 static double box3d_normalize_angle (double a) {
2697 double angle = a + ((int) (a/360.0))*360;
2698 if (angle < 0) {
2699 angle += 360.0;
2700 }
2701 return angle;
2702 }
2704 static void
2705 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2706 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2707 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2708 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2709 // are reset).
2710 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2712 if (is_infinite) {
2713 gtk_toggle_action_set_active(tact, TRUE);
2714 gtk_action_set_sensitive(act, TRUE);
2716 double angle = persp3d_get_infinite_angle(persp, axis);
2717 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2718 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2719 }
2720 } else {
2721 gtk_toggle_action_set_active(tact, FALSE);
2722 gtk_action_set_sensitive(act, FALSE);
2723 }
2724 }
2726 static void
2727 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2728 if (!persp_repr) {
2729 g_print ("No perspective given to box3d_resync_toolbar().\n");
2730 return;
2731 }
2733 GtkWidget *tbl = GTK_WIDGET(data);
2734 GtkAdjustment *adj = 0;
2735 GtkAction *act = 0;
2736 GtkToggleAction *tact = 0;
2737 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2738 {
2739 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2740 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2741 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2743 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2744 }
2745 {
2746 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2747 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2748 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2750 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2751 }
2752 {
2753 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2754 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2755 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2757 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2758 }
2759 }
2761 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2762 gchar const */*old_value*/, gchar const */*new_value*/,
2763 bool /*is_interactive*/, gpointer data)
2764 {
2765 GtkWidget *tbl = GTK_WIDGET(data);
2767 // quit if run by the attr_changed listener
2768 // note: it used to work without the differently called freeze_ attributes (here and in
2769 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2770 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2771 return;
2772 }
2774 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2775 // sp_document_maybe_done() when the document is undo insensitive)
2776 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2778 // TODO: Only update the appropriate part of the toolbar
2779 // if (!strcmp(name, "inkscape:vp_z")) {
2780 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2781 // }
2783 Persp3D *persp = persp3d_get_from_repr(repr);
2784 persp3d_update_box_reprs(persp);
2786 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2787 }
2789 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2790 {
2791 NULL, /* child_added */
2792 NULL, /* child_removed */
2793 box3d_persp_tb_event_attr_changed,
2794 NULL, /* content_changed */
2795 NULL /* order_changed */
2796 };
2798 /**
2799 * \param selection Should not be NULL.
2800 */
2801 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2802 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2803 static void
2804 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2805 {
2806 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2807 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2808 // update the perspectives with infinite VPs and leave the other ones untouched).
2810 Inkscape::XML::Node *persp_repr = NULL;
2811 purge_repr_listener(tbl, tbl);
2813 SPItem *item = selection->singleItem();
2814 if (item && SP_IS_BOX3D(item)) {
2815 // FIXME: Also deal with multiple selected boxes
2816 SPBox3D *box = SP_BOX3D(item);
2817 Persp3D *persp = box3d_get_perspective(box);
2818 persp_repr = SP_OBJECT_REPR(persp);
2819 if (persp_repr) {
2820 g_object_set_data(tbl, "repr", persp_repr);
2821 Inkscape::GC::anchor(persp_repr);
2822 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2823 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2824 }
2826 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2827 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2829 box3d_resync_toolbar(persp_repr, tbl);
2830 }
2831 }
2833 static void
2834 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2835 {
2836 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2837 SPDocument *document = sp_desktop_document(desktop);
2839 // quit if run by the attr_changed listener
2840 // note: it used to work without the differently called freeze_ attributes (here and in
2841 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2842 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2843 return;
2844 }
2846 // in turn, prevent listener from responding
2847 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2849 //Persp3D *persp = document->current_persp3d;
2850 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2851 if (sel_persps.empty()) {
2852 // this can happen when the document is created; we silently ignore it
2853 return;
2854 }
2855 Persp3D *persp = sel_persps.front();
2857 persp->tmat.set_infinite_direction (axis, adj->value);
2858 SP_OBJECT(persp)->updateRepr();
2860 // TODO: use the correct axis here, too
2861 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2863 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2864 }
2867 static void
2868 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2869 {
2870 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2871 }
2873 static void
2874 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2875 {
2876 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2877 }
2879 static void
2880 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2881 {
2882 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2883 }
2886 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2887 {
2888 // TODO: Take all selected perspectives into account
2889 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2890 if (sel_persps.empty()) {
2891 // this can happen when the document is created; we silently ignore it
2892 return;
2893 }
2894 Persp3D *persp = sel_persps.front();
2896 bool set_infinite = gtk_toggle_action_get_active(act);
2897 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2898 }
2900 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2901 {
2902 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2903 }
2905 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2906 {
2907 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2908 }
2910 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2911 {
2912 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2913 }
2915 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2916 {
2917 EgeAdjustmentAction* eact = 0;
2918 SPDocument *document = sp_desktop_document (desktop);
2919 Persp3D *persp = document->current_persp3d;
2921 EgeAdjustmentAction* box3d_angle_x = 0;
2922 EgeAdjustmentAction* box3d_angle_y = 0;
2923 EgeAdjustmentAction* box3d_angle_z = 0;
2925 /* Angle X */
2926 {
2927 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2928 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2929 eact = create_adjustment_action( "3DBoxAngleXAction",
2930 _("Angle in X direction"), _("Angle X:"),
2931 // Translators: PL is short for 'perspective line'
2932 _("Angle of PLs in X direction"),
2933 "tools.shapes.3dbox", "box3d_angle_x", 30,
2934 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2935 -360.0, 360.0, 1.0, 10.0,
2936 labels, values, G_N_ELEMENTS(labels),
2937 box3d_angle_x_value_changed );
2938 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2939 g_object_set_data( holder, "box3d_angle_x_action", eact );
2940 box3d_angle_x = eact;
2941 }
2943 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2944 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2945 } else {
2946 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2947 }
2950 /* VP X state */
2951 {
2952 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2953 // Translators: VP is short for 'vanishing point'
2954 _("State of VP in X direction"),
2955 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2956 "toggle_vp_x",
2957 Inkscape::ICON_SIZE_DECORATION );
2958 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2959 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2960 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2961 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2962 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2963 }
2965 /* Angle Y */
2966 {
2967 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2968 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2969 eact = create_adjustment_action( "3DBoxAngleYAction",
2970 _("Angle in Y direction"), _("Angle Y:"),
2971 // Translators: PL is short for 'perspective line'
2972 _("Angle of PLs in Y direction"),
2973 "tools.shapes.3dbox", "box3d_angle_y", 30,
2974 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2975 -360.0, 360.0, 1.0, 10.0,
2976 labels, values, G_N_ELEMENTS(labels),
2977 box3d_angle_y_value_changed );
2978 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2979 g_object_set_data( holder, "box3d_angle_y_action", eact );
2980 box3d_angle_y = eact;
2981 }
2983 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2984 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2985 } else {
2986 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2987 }
2989 /* VP Y state */
2990 {
2991 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2992 // Translators: VP is short for 'vanishing point'
2993 _("State of VP in Y direction"),
2994 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2995 "toggle_vp_y",
2996 Inkscape::ICON_SIZE_DECORATION );
2997 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2998 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2999 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3000 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
3001 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
3002 }
3004 /* Angle Z */
3005 {
3006 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3007 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3008 eact = create_adjustment_action( "3DBoxAngleZAction",
3009 _("Angle in Z direction"), _("Angle Z:"),
3010 // Translators: PL is short for 'perspective line'
3011 _("Angle of PLs in Z direction"),
3012 "tools.shapes.3dbox", "box3d_angle_z", 30,
3013 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3014 -360.0, 360.0, 1.0, 10.0,
3015 labels, values, G_N_ELEMENTS(labels),
3016 box3d_angle_z_value_changed );
3017 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3018 g_object_set_data( holder, "box3d_angle_z_action", eact );
3019 box3d_angle_z = eact;
3020 }
3022 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3023 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3024 } else {
3025 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3026 }
3028 /* VP Z state */
3029 {
3030 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3031 // Translators: VP is short for 'vanishing point'
3032 _("State of VP in Z direction"),
3033 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3034 "toggle_vp_z",
3035 Inkscape::ICON_SIZE_DECORATION );
3036 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3037 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3038 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3039 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3040 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3041 }
3043 sigc::connection *connection = new sigc::connection(
3044 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3045 );
3046 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3047 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3048 }
3050 //########################
3051 //## Spiral ##
3052 //########################
3054 static void
3055 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3056 {
3057 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3059 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3060 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3061 }
3063 // quit if run by the attr_changed listener
3064 if (g_object_get_data( tbl, "freeze" )) {
3065 return;
3066 }
3068 // in turn, prevent listener from responding
3069 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3071 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3073 bool modmade = false;
3074 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3075 items != NULL;
3076 items = items->next)
3077 {
3078 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3079 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3080 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3081 SP_OBJECT((SPItem *) items->data)->updateRepr();
3082 modmade = true;
3083 }
3084 }
3086 g_free(namespaced_name);
3088 if (modmade) {
3089 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3090 _("Change spiral"));
3091 }
3093 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3094 }
3096 static void
3097 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3098 {
3099 sp_spl_tb_value_changed(adj, tbl, "revolution");
3100 }
3102 static void
3103 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3104 {
3105 sp_spl_tb_value_changed(adj, tbl, "expansion");
3106 }
3108 static void
3109 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3110 {
3111 sp_spl_tb_value_changed(adj, tbl, "t0");
3112 }
3114 static void
3115 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3116 {
3117 GtkWidget *tbl = GTK_WIDGET(obj);
3119 GtkAdjustment *adj;
3121 // fixme: make settable
3122 gdouble rev = 5;
3123 gdouble exp = 1.0;
3124 gdouble t0 = 0.0;
3126 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3127 gtk_adjustment_set_value(adj, rev);
3128 gtk_adjustment_value_changed(adj);
3130 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3131 gtk_adjustment_set_value(adj, exp);
3132 gtk_adjustment_value_changed(adj);
3134 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3135 gtk_adjustment_set_value(adj, t0);
3136 gtk_adjustment_value_changed(adj);
3138 spinbutton_defocus(GTK_OBJECT(tbl));
3139 }
3142 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3143 gchar const */*old_value*/, gchar const */*new_value*/,
3144 bool /*is_interactive*/, gpointer data)
3145 {
3146 GtkWidget *tbl = GTK_WIDGET(data);
3148 // quit if run by the _changed callbacks
3149 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3150 return;
3151 }
3153 // in turn, prevent callbacks from responding
3154 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3156 GtkAdjustment *adj;
3157 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3158 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3160 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3161 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3163 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3164 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3166 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3167 }
3170 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3171 NULL, /* child_added */
3172 NULL, /* child_removed */
3173 spiral_tb_event_attr_changed,
3174 NULL, /* content_changed */
3175 NULL /* order_changed */
3176 };
3178 static void
3179 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3180 {
3181 int n_selected = 0;
3182 Inkscape::XML::Node *repr = NULL;
3184 purge_repr_listener( tbl, tbl );
3186 for (GSList const *items = selection->itemList();
3187 items != NULL;
3188 items = items->next)
3189 {
3190 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3191 n_selected++;
3192 repr = SP_OBJECT_REPR((SPItem *) items->data);
3193 }
3194 }
3196 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3198 if (n_selected == 0) {
3199 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3200 } else if (n_selected == 1) {
3201 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3203 if (repr) {
3204 g_object_set_data( tbl, "repr", repr );
3205 Inkscape::GC::anchor(repr);
3206 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3207 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3208 }
3209 } else {
3210 // FIXME: implement averaging of all parameters for multiple selected
3211 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3212 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3213 }
3214 }
3217 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3218 {
3219 EgeAdjustmentAction* eact = 0;
3220 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3222 {
3223 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3224 ege_output_action_set_use_markup( act, TRUE );
3225 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3226 g_object_set_data( holder, "mode_action", act );
3227 }
3229 /* Revolution */
3230 {
3231 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3232 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3233 eact = create_adjustment_action( "SpiralRevolutionAction",
3234 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3235 "tools.shapes.spiral", "revolution", 3.0,
3236 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3237 0.01, 1024.0, 0.1, 1.0,
3238 labels, values, G_N_ELEMENTS(labels),
3239 sp_spl_tb_revolution_value_changed, 1, 2);
3240 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3241 }
3243 /* Expansion */
3244 {
3245 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3246 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3247 eact = create_adjustment_action( "SpiralExpansionAction",
3248 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3249 "tools.shapes.spiral", "expansion", 1.0,
3250 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3251 0.0, 1000.0, 0.01, 1.0,
3252 labels, values, G_N_ELEMENTS(labels),
3253 sp_spl_tb_expansion_value_changed);
3254 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3255 }
3257 /* T0 */
3258 {
3259 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3260 gdouble values[] = {0, 0.5, 0.9};
3261 eact = create_adjustment_action( "SpiralT0Action",
3262 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3263 "tools.shapes.spiral", "t0", 0.0,
3264 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3265 0.0, 0.999, 0.01, 1.0,
3266 labels, values, G_N_ELEMENTS(labels),
3267 sp_spl_tb_t0_value_changed);
3268 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3269 }
3271 /* Reset */
3272 {
3273 InkAction* inky = ink_action_new( "SpiralResetAction",
3274 _("Defaults"),
3275 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3276 GTK_STOCK_CLEAR,
3277 secondarySize );
3278 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3279 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3280 }
3283 sigc::connection *connection = new sigc::connection(
3284 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3285 );
3286 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3287 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3288 }
3290 //########################
3291 //## Pen/Pencil ##
3292 //########################
3294 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3295 static char const *
3296 freehand_tool_name(GObject *dataKludge)
3297 {
3298 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3299 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3300 ? "tools.freehand.pen"
3301 : "tools.freehand.pencil" );
3302 }
3304 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3305 {
3306 gint mode = ege_select_one_action_get_active(act);
3308 prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3310 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3312 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3313 // preparatory work here
3314 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3315 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3316 sp_pen_context_set_polyline_mode(pc);
3317 }
3318 }
3320 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3321 {
3322 /* Freehand mode toggle buttons */
3323 {
3324 guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3325 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3327 {
3328 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3330 GtkTreeIter iter;
3331 gtk_list_store_append( model, &iter );
3332 gtk_list_store_set( model, &iter,
3333 0, _("Bezier"),
3334 1, _("Create regular Bezier path"),
3335 2, "bezier_mode",
3336 -1 );
3338 gtk_list_store_append( model, &iter );
3339 gtk_list_store_set( model, &iter,
3340 0, _("Spiro"),
3341 1, _("Create Spiro path"),
3342 2, "spiro_splines_mode",
3343 -1 );
3345 if (!tool_is_pencil) {
3346 gtk_list_store_append( model, &iter );
3347 gtk_list_store_set( model, &iter,
3348 0, _("Zigzag"),
3349 1, _("Create a sequence of straight line segments"),
3350 2, "polylines_mode",
3351 -1 );
3353 gtk_list_store_append( model, &iter );
3354 gtk_list_store_set( model, &iter,
3355 0, _("Paraxial"),
3356 1, _("Create a sequence of paraxial line segments"),
3357 2, "paraxial_lines_mode",
3358 -1 );
3359 }
3361 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3362 "FreehandModeActionPencil" :
3363 "FreehandModeActionPen",
3364 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3365 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3367 ege_select_one_action_set_appearance( act, "full" );
3368 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3369 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3370 ege_select_one_action_set_icon_column( act, 2 );
3371 ege_select_one_action_set_icon_size( act, secondarySize );
3372 ege_select_one_action_set_tooltip_column( act, 1 );
3374 ege_select_one_action_set_active( act, freehandMode);
3375 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3376 }
3377 }
3378 }
3380 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3381 gint shape = ege_select_one_action_get_active( act );
3382 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3383 }
3385 /**
3386 * \brief Generate the list of freehand advanced shape option entries.
3387 */
3388 GList * freehand_shape_dropdown_items_list() {
3389 GList *glist = NULL;
3391 glist = g_list_append (glist, _("None"));
3392 glist = g_list_append (glist, _("Triangle in"));
3393 glist = g_list_append (glist, _("Triangle out"));
3394 glist = g_list_append (glist, _("Ellipse"));
3395 glist = g_list_append (glist, _("From clipboard"));
3397 return glist;
3398 }
3400 static void
3401 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3402 /*advanced shape options */
3403 {
3404 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3406 GList* items = 0;
3407 gint count = 0;
3408 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3409 {
3410 GtkTreeIter iter;
3411 gtk_list_store_append( model, &iter );
3412 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3413 count++;
3414 }
3415 g_list_free( items );
3416 items = 0;
3417 EgeSelectOneAction* act1 = ege_select_one_action_new(
3418 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3419 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3420 g_object_set( act1, "short_label", _("Shape:"), NULL );
3421 ege_select_one_action_set_appearance( act1, "compact" );
3422 ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3423 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3424 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3425 g_object_set_data( holder, "shape_action", act1 );
3426 }
3427 }
3429 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3430 {
3431 sp_add_freehand_mode_toggle(mainActions, holder, false);
3432 freehand_add_advanced_shape_options(mainActions, holder, false);
3433 }
3436 static void
3437 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3438 {
3439 GtkWidget *tbl = GTK_WIDGET(obj);
3441 GtkAdjustment *adj;
3443 // fixme: make settable
3444 gdouble tolerance = 4;
3446 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3447 gtk_adjustment_set_value(adj, tolerance);
3448 gtk_adjustment_value_changed(adj);
3450 spinbutton_defocus(GTK_OBJECT(tbl));
3451 }
3453 static void
3454 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3455 {
3457 // quit if run by the attr_changed listener
3458 if (g_object_get_data( tbl, "freeze" )) {
3459 return;
3460 }
3461 // in turn, prevent listener from responding
3462 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3463 prefs_set_double_attribute("tools.freehand.pencil",
3464 "tolerance", adj->value);
3465 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3467 }
3471 static void
3472 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3473 const gchar */*key*/,
3474 const gchar */*oldval*/,
3475 const gchar */*newval*/,
3476 bool /*is_interactive*/,
3477 void * data)
3478 {
3479 GObject* tbl = G_OBJECT(data);
3480 if (g_object_get_data( tbl, "freeze" )) {
3481 return;
3482 }
3484 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3486 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3487 "tolerance");
3489 double v = prefs_get_double_attribute("tools.freehand.pencil",
3490 "tolerance", adj->value);
3491 gtk_adjustment_set_value(adj, v);
3492 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3494 }
3496 static Inkscape::XML::NodeEventVector pencil_node_events =
3497 {
3498 NULL,
3499 NULL,
3500 sp_pencil_tb_tolerance_value_changed_external,
3501 NULL,
3502 NULL,
3503 };
3506 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3507 {
3508 sp_add_freehand_mode_toggle(mainActions, holder, true);
3510 EgeAdjustmentAction* eact = 0;
3512 /* Tolerance */
3513 {
3514 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3515 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3516 eact = create_adjustment_action( "PencilToleranceAction",
3517 _("Smoothing:"), _("Smoothing: "),
3518 _("How much smoothing (simplifying) is applied to the line"),
3519 "tools.freehand.pencil", "tolerance",
3520 3.0,
3521 GTK_WIDGET(desktop->canvas), NULL,
3522 holder, TRUE, "altx-pencil",
3523 1, 100.0, 0.5, 0,
3524 labels, values, G_N_ELEMENTS(labels),
3525 sp_pencil_tb_tolerance_value_changed,
3526 1, 2);
3527 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3528 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3530 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3531 "tools.freehand.pencil");
3532 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3533 g_object_set_data(G_OBJECT(holder), "repr", repr);
3535 }
3537 /* advanced shape options */
3538 freehand_add_advanced_shape_options(mainActions, holder, true);
3540 /* Reset */
3541 {
3542 InkAction* inky = ink_action_new( "PencilResetAction",
3543 _("Defaults"),
3544 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3545 GTK_STOCK_CLEAR,
3546 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3547 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3548 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3549 }
3551 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3553 }
3556 //########################
3557 //## Tweak ##
3558 //########################
3560 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3561 {
3562 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3563 }
3565 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3566 {
3567 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3568 }
3570 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3571 {
3572 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3573 }
3575 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3576 {
3577 int mode = ege_select_one_action_get_active( act );
3578 prefs_set_int_attribute("tools.tweak", "mode", mode);
3580 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3581 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3582 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3583 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3584 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3585 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3586 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3587 if (doh) gtk_action_set_sensitive (doh, TRUE);
3588 if (dos) gtk_action_set_sensitive (dos, TRUE);
3589 if (dol) gtk_action_set_sensitive (dol, TRUE);
3590 if (doo) gtk_action_set_sensitive (doo, TRUE);
3591 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3592 if (fid) gtk_action_set_sensitive (fid, FALSE);
3593 } else {
3594 if (doh) gtk_action_set_sensitive (doh, FALSE);
3595 if (dos) gtk_action_set_sensitive (dos, FALSE);
3596 if (dol) gtk_action_set_sensitive (dol, FALSE);
3597 if (doo) gtk_action_set_sensitive (doo, FALSE);
3598 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3599 if (fid) gtk_action_set_sensitive (fid, TRUE);
3600 }
3601 }
3603 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3604 {
3605 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3606 }
3608 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3609 bool show = gtk_toggle_action_get_active( act );
3610 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3611 }
3612 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3613 bool show = gtk_toggle_action_get_active( act );
3614 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3615 }
3616 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3617 bool show = gtk_toggle_action_get_active( act );
3618 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3619 }
3620 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3621 bool show = gtk_toggle_action_get_active( act );
3622 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3623 }
3625 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3626 {
3627 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3629 {
3630 /* Width */
3631 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3632 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3633 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3634 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3635 "tools.tweak", "width", 15,
3636 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3637 1, 100, 1.0, 0.0,
3638 labels, values, G_N_ELEMENTS(labels),
3639 sp_tweak_width_value_changed, 0.01, 0, 100 );
3640 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3641 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3642 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3643 }
3646 {
3647 /* Force */
3648 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3649 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3650 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3651 _("Force"), _("Force:"), _("The force of the tweak action"),
3652 "tools.tweak", "force", 20,
3653 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3654 1, 100, 1.0, 0.0,
3655 labels, values, G_N_ELEMENTS(labels),
3656 sp_tweak_force_value_changed, 0.01, 0, 100 );
3657 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3658 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3659 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3660 }
3662 /* Mode */
3663 {
3664 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3666 GtkTreeIter iter;
3667 gtk_list_store_append( model, &iter );
3668 gtk_list_store_set( model, &iter,
3669 0, _("Push mode"),
3670 1, _("Push parts of paths in any direction"),
3671 2, "tweak_push_mode",
3672 -1 );
3674 gtk_list_store_append( model, &iter );
3675 gtk_list_store_set( model, &iter,
3676 0, _("Shrink mode"),
3677 1, _("Shrink (inset) parts of paths"),
3678 2, "tweak_shrink_mode",
3679 -1 );
3681 gtk_list_store_append( model, &iter );
3682 gtk_list_store_set( model, &iter,
3683 0, _("Grow mode"),
3684 1, _("Grow (outset) parts of paths"),
3685 2, "tweak_grow_mode",
3686 -1 );
3688 gtk_list_store_append( model, &iter );
3689 gtk_list_store_set( model, &iter,
3690 0, _("Attract mode"),
3691 1, _("Attract parts of paths towards cursor"),
3692 2, "tweak_attract_mode",
3693 -1 );
3695 gtk_list_store_append( model, &iter );
3696 gtk_list_store_set( model, &iter,
3697 0, _("Repel mode"),
3698 1, _("Repel parts of paths from cursor"),
3699 2, "tweak_repel_mode",
3700 -1 );
3702 gtk_list_store_append( model, &iter );
3703 gtk_list_store_set( model, &iter,
3704 0, _("Roughen mode"),
3705 1, _("Roughen parts of paths"),
3706 2, "tweak_roughen_mode",
3707 -1 );
3709 gtk_list_store_append( model, &iter );
3710 gtk_list_store_set( model, &iter,
3711 0, _("Color paint mode"),
3712 1, _("Paint the tool's color upon selected objects"),
3713 2, "tweak_colorpaint_mode",
3714 -1 );
3716 gtk_list_store_append( model, &iter );
3717 gtk_list_store_set( model, &iter,
3718 0, _("Color jitter mode"),
3719 1, _("Jitter the colors of selected objects"),
3720 2, "tweak_colorjitter_mode",
3721 -1 );
3723 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3724 g_object_set( act, "short_label", _("Mode:"), NULL );
3725 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3726 g_object_set_data( holder, "mode_action", act );
3728 ege_select_one_action_set_appearance( act, "full" );
3729 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3730 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3731 ege_select_one_action_set_icon_column( act, 2 );
3732 ege_select_one_action_set_icon_size( act, secondarySize );
3733 ege_select_one_action_set_tooltip_column( act, 1 );
3735 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3736 ege_select_one_action_set_active( act, mode );
3737 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3739 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3740 }
3742 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3744 {
3745 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3746 ege_output_action_set_use_markup( act, TRUE );
3747 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3748 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3749 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3750 g_object_set_data( holder, "tweak_channels_label", act);
3751 }
3753 {
3754 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3755 _("Hue"),
3756 _("In color mode, act on objects' hue"),
3757 NULL,
3758 Inkscape::ICON_SIZE_DECORATION );
3759 //TRANSLATORS: "H" here stands for hue
3760 g_object_set( act, "short_label", _("H"), NULL );
3761 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3762 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3763 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3764 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3765 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3766 g_object_set_data( holder, "tweak_doh", act);
3767 }
3768 {
3769 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3770 _("Saturation"),
3771 _("In color mode, act on objects' saturation"),
3772 NULL,
3773 Inkscape::ICON_SIZE_DECORATION );
3774 //TRANSLATORS: "S" here stands for Saturation
3775 g_object_set( act, "short_label", _("S"), NULL );
3776 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3777 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3778 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3779 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3780 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3781 g_object_set_data( holder, "tweak_dos", act );
3782 }
3783 {
3784 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3785 _("Lightness"),
3786 _("In color mode, act on objects' lightness"),
3787 NULL,
3788 Inkscape::ICON_SIZE_DECORATION );
3789 //TRANSLATORS: "L" here stands for Lightness
3790 g_object_set( act, "short_label", _("L"), NULL );
3791 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3792 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3793 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3794 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3795 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3796 g_object_set_data( holder, "tweak_dol", act );
3797 }
3798 {
3799 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3800 _("Opacity"),
3801 _("In color mode, act on objects' opacity"),
3802 NULL,
3803 Inkscape::ICON_SIZE_DECORATION );
3804 //TRANSLATORS: "O" here stands for Opacity
3805 g_object_set( act, "short_label", _("O"), NULL );
3806 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3807 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3808 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3809 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3810 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3811 g_object_set_data( holder, "tweak_doo", act );
3812 }
3814 { /* Fidelity */
3815 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3816 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3817 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3818 _("Fidelity"), _("Fidelity:"),
3819 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3820 "tools.tweak", "fidelity", 50,
3821 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3822 1, 100, 1.0, 10.0,
3823 labels, values, G_N_ELEMENTS(labels),
3824 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3825 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3826 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3827 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3828 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3829 g_object_set_data( holder, "tweak_fidelity", eact );
3830 }
3833 /* Use Pressure button */
3834 {
3835 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3836 _("Pressure"),
3837 _("Use the pressure of the input device to alter the force of tweak action"),
3838 "use_pressure",
3839 Inkscape::ICON_SIZE_DECORATION );
3840 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3841 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3842 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3843 }
3845 }
3848 //########################
3849 //## Calligraphy ##
3850 //########################
3851 static void update_presets_list (GObject *tbl)
3852 {
3853 if (g_object_get_data(tbl, "presets_blocked"))
3854 return;
3856 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3857 if (!sel) {
3858 ege_select_one_action_set_active(sel, 0);
3859 return;
3860 }
3862 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3864 for (int i = 1; i <= total_prefs; i++) {
3865 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3866 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3868 bool match = true;
3870 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3871 iter;
3872 ++iter ) {
3873 const gchar *attr_name = g_quark_to_string(iter->key);
3874 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3875 continue;
3876 void *widget = g_object_get_data(tbl, attr_name);
3877 if (widget) {
3878 if (GTK_IS_ADJUSTMENT(widget)) {
3879 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3880 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3881 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3882 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3883 match = false;
3884 break;
3885 }
3886 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3887 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3888 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3889 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3890 if (gtk_toggle_action_get_active(toggle) != v) {
3891 match = false;
3892 break;
3893 }
3894 }
3895 }
3896 }
3898 if (match) {
3899 // newly added item is at the same index as the
3900 // save command, so we need to change twice for it to take effect
3901 ege_select_one_action_set_active(sel, 0);
3902 ege_select_one_action_set_active(sel, i);
3903 return;
3904 }
3905 }
3907 // no match found
3908 ege_select_one_action_set_active(sel, 0);
3909 }
3911 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3912 {
3913 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3914 update_presets_list(tbl);
3915 }
3917 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3918 {
3919 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3920 update_presets_list(tbl);
3921 }
3923 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3924 {
3925 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3926 update_presets_list(tbl);
3927 }
3929 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3930 {
3931 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3932 update_presets_list(tbl);
3933 }
3935 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3936 {
3937 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3938 update_presets_list(tbl);
3939 }
3941 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3942 {
3943 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3944 update_presets_list(tbl);
3945 }
3947 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3948 {
3949 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3950 update_presets_list(tbl);
3951 }
3953 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3954 {
3955 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3956 update_presets_list(tbl);
3957 }
3959 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3960 {
3961 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3962 update_presets_list(tbl);
3963 }
3965 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3966 {
3967 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3968 update_presets_list(tbl);
3969 }
3971 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3972 {
3973 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3974 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3975 update_presets_list(tbl);
3976 if (calligraphy_angle )
3977 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3978 }
3981 static gchar const *const widget_names[] = {
3982 "width",
3983 "mass",
3984 "wiggle",
3985 "angle",
3986 "thinning",
3987 "tremor",
3988 "flatness",
3989 "cap_rounding",
3990 "usepressure",
3991 "tracebackground",
3992 "usetilt"
3993 };
3996 static void sp_dcc_build_presets_list(GObject *tbl)
3997 {
3998 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4000 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4001 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4002 gtk_list_store_clear (model);
4004 {
4005 GtkTreeIter iter;
4006 gtk_list_store_append( model, &iter );
4007 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4008 }
4010 //TODO: switch back to prefs API
4011 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4012 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4013 int ii=1;
4014 while (child_repr) {
4015 GtkTreeIter iter;
4016 char *preset_name = (char *) child_repr->attribute("name");
4017 gtk_list_store_append( model, &iter );
4018 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4019 child_repr = sp_repr_next(child_repr);
4020 }
4022 {
4023 GtkTreeIter iter;
4024 gtk_list_store_append( model, &iter );
4025 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4026 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4027 }
4029 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4031 update_presets_list (tbl);
4032 }
4034 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4035 {
4036 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4037 if (! desktop) return;
4039 if (g_object_get_data(tbl, "presets_blocked"))
4040 return;
4042 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4043 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4044 // dialog cancelled
4045 update_presets_list (tbl);
4046 return;
4047 }
4048 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4050 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4051 // empty name entered
4052 update_presets_list (tbl);
4053 return;
4054 }
4056 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4058 int new_index = -1;
4059 gchar *pref_path = NULL;
4060 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4062 for (int i = 1; i <= total_prefs; i++) {
4063 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4064 const gchar *name = prefs_get_string_attribute(path, "name");
4065 if (name && !strcmp(name, profile_name.c_str())) {
4066 // we already have preset with this name, replace its values
4067 new_index = i;
4068 pref_path = g_strdup(path);
4069 break;
4070 }
4071 }
4073 if (new_index == -1) {
4074 // no preset with this name, create
4075 new_index = total_prefs + 1;
4076 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4077 pref_path = create_pref("tools.calligraphic.preset", profile_id);
4078 g_free(profile_id);
4079 }
4081 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4082 gchar const *const widget_name = widget_names[i];
4083 void *widget = g_object_get_data(tbl, widget_name);
4084 if (widget) {
4085 if (GTK_IS_ADJUSTMENT(widget)) {
4086 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4087 double v = gtk_adjustment_get_value(adj);
4088 prefs_set_double_attribute(pref_path, widget_name, v);
4089 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4090 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4091 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4092 int v = gtk_toggle_action_get_active(toggle);
4093 prefs_set_int_attribute(pref_path, widget_name, v);
4094 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4095 } else {
4096 g_warning("Unknown widget type for preset: %s\n", widget_name);
4097 }
4098 } else {
4099 g_warning("Bad key when writing preset: %s\n", widget_name);
4100 }
4101 }
4102 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4104 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4106 sp_dcc_build_presets_list (tbl);
4108 g_free(pref_path);
4109 }
4112 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4114 gint preset_index = ege_select_one_action_get_active( act );
4115 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4117 if (preset_index == save_presets_index) {
4118 // this is the Save command
4119 sp_dcc_save_profile(NULL, tbl);
4120 return;
4121 }
4123 if (g_object_get_data(tbl, "presets_blocked"))
4124 return;
4126 gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4128 if (preset_path) {
4129 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
4131 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4133 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4134 iter;
4135 ++iter ) {
4136 const gchar *attr_name = g_quark_to_string(iter->key);
4137 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4138 continue;
4139 void *widget = g_object_get_data(tbl, attr_name);
4140 if (widget) {
4141 if (GTK_IS_ADJUSTMENT(widget)) {
4142 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4143 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4144 gtk_adjustment_set_value(adj, v);
4145 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4146 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4147 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4148 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4149 gtk_toggle_action_set_active(toggle, v);
4150 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4151 } else {
4152 g_warning("Unknown widget type for preset: %s\n", attr_name);
4153 }
4154 } else {
4155 g_warning("Bad key found in a preset record: %s\n", attr_name);
4156 }
4157 }
4158 g_free(preset_path);
4159 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4160 }
4162 }
4165 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4166 {
4167 {
4168 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4170 EgeAdjustmentAction* calligraphy_angle = 0;
4172 {
4173 /* Width */
4174 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4175 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4176 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4177 _("Pen Width"), _("Width:"),
4178 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4179 "tools.calligraphic", "width", 15,
4180 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4181 1, 100, 1.0, 0.0,
4182 labels, values, G_N_ELEMENTS(labels),
4183 sp_ddc_width_value_changed, 0.01, 0, 100 );
4184 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4185 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4186 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4187 }
4189 {
4190 /* Thinning */
4191 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4192 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4193 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4194 _("Stroke Thinning"), _("Thinning:"),
4195 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4196 "tools.calligraphic", "thinning", 10,
4197 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4198 -100, 100, 1, 0.1,
4199 labels, values, G_N_ELEMENTS(labels),
4200 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4201 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4202 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4203 }
4205 {
4206 /* Angle */
4207 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4208 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4209 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4210 _("Pen Angle"), _("Angle:"),
4211 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4212 "tools.calligraphic", "angle", 30,
4213 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4214 -90.0, 90.0, 1.0, 10.0,
4215 labels, values, G_N_ELEMENTS(labels),
4216 sp_ddc_angle_value_changed, 1, 0 );
4217 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4218 g_object_set_data( holder, "angle_action", eact );
4219 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4220 calligraphy_angle = eact;
4221 }
4223 {
4224 /* Fixation */
4225 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4226 gdouble values[] = {0, 20, 40, 60, 90, 100};
4227 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4228 _("Fixation"), _("Fixation:"),
4229 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4230 "tools.calligraphic", "flatness", 90,
4231 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4232 0.0, 100, 1.0, 10.0,
4233 labels, values, G_N_ELEMENTS(labels),
4234 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4235 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4236 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4237 }
4239 {
4240 /* Cap Rounding */
4241 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4242 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4243 // TRANSLATORS: "cap" means "end" (both start and finish) here
4244 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4245 _("Cap rounding"), _("Caps:"),
4246 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4247 "tools.calligraphic", "cap_rounding", 0.0,
4248 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4249 0.0, 5.0, 0.01, 0.1,
4250 labels, values, G_N_ELEMENTS(labels),
4251 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4252 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4253 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4254 }
4256 {
4257 /* Tremor */
4258 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4259 gdouble values[] = {0, 10, 20, 40, 60, 100};
4260 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4261 _("Stroke Tremor"), _("Tremor:"),
4262 _("Increase to make strokes rugged and trembling"),
4263 "tools.calligraphic", "tremor", 0.0,
4264 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4265 0.0, 100, 1, 0.0,
4266 labels, values, G_N_ELEMENTS(labels),
4267 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4269 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4270 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4271 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4272 }
4274 {
4275 /* Wiggle */
4276 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4277 gdouble values[] = {0, 20, 40, 60, 100};
4278 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4279 _("Pen Wiggle"), _("Wiggle:"),
4280 _("Increase to make the pen waver and wiggle"),
4281 "tools.calligraphic", "wiggle", 0.0,
4282 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4283 0.0, 100, 1, 0.0,
4284 labels, values, G_N_ELEMENTS(labels),
4285 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4286 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4287 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4288 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4289 }
4291 {
4292 /* Mass */
4293 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4294 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4295 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4296 _("Pen Mass"), _("Mass:"),
4297 _("Increase to make the pen drag behind, as if slowed by inertia"),
4298 "tools.calligraphic", "mass", 2.0,
4299 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4300 0.0, 100, 1, 0.0,
4301 labels, values, G_N_ELEMENTS(labels),
4302 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4303 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4304 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4305 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4306 }
4309 /* Trace Background button */
4310 {
4311 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4312 _("Trace Background"),
4313 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4314 "trace_background",
4315 Inkscape::ICON_SIZE_DECORATION );
4316 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4317 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4318 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4319 g_object_set_data( holder, "tracebackground", act );
4320 }
4322 /* Use Pressure button */
4323 {
4324 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4325 _("Pressure"),
4326 _("Use the pressure of the input device to alter the width of the pen"),
4327 "use_pressure",
4328 Inkscape::ICON_SIZE_DECORATION );
4329 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4330 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4331 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4332 g_object_set_data( holder, "usepressure", act );
4333 }
4335 /* Use Tilt button */
4336 {
4337 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4338 _("Tilt"),
4339 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4340 "use_tilt",
4341 Inkscape::ICON_SIZE_DECORATION );
4342 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4343 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4344 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4345 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4346 g_object_set_data( holder, "usetilt", act );
4347 }
4349 /*calligraphic profile */
4350 {
4351 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4352 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4353 ege_select_one_action_set_appearance (act1, "compact");
4354 g_object_set_data (holder, "profile_selector", act1 );
4356 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4358 sp_dcc_build_presets_list (holder);
4360 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4361 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4362 }
4363 }
4364 }
4367 //########################
4368 //## Circle / Arc ##
4369 //########################
4371 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4372 {
4373 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4374 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4376 if (v1 == 0 && v2 == 0) {
4377 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4378 gtk_action_set_sensitive( ocb, FALSE );
4379 gtk_action_set_sensitive( make_whole, FALSE );
4380 }
4381 } else {
4382 gtk_action_set_sensitive( ocb, TRUE );
4383 gtk_action_set_sensitive( make_whole, TRUE );
4384 }
4385 }
4387 static void
4388 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4389 {
4390 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4392 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4393 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4394 }
4396 // quit if run by the attr_changed listener
4397 if (g_object_get_data( tbl, "freeze" )) {
4398 return;
4399 }
4401 // in turn, prevent listener from responding
4402 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4404 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4406 bool modmade = false;
4407 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4408 items != NULL;
4409 items = items->next)
4410 {
4411 SPItem *item = SP_ITEM(items->data);
4413 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4415 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4416 SPArc *arc = SP_ARC(item);
4418 if (!strcmp(value_name, "start"))
4419 ge->start = (adj->value * M_PI)/ 180;
4420 else
4421 ge->end = (adj->value * M_PI)/ 180;
4423 sp_genericellipse_normalize(ge);
4424 ((SPObject *)arc)->updateRepr();
4425 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4427 modmade = true;
4428 }
4429 }
4431 g_free(namespaced_name);
4433 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4435 sp_arctb_sensitivize( tbl, adj->value, other->value );
4437 if (modmade) {
4438 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4439 _("Arc: Change start/end"));
4440 }
4442 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4443 }
4446 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4447 {
4448 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4449 }
4451 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4452 {
4453 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4454 }
4457 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4458 {
4459 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4460 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4461 if ( ege_select_one_action_get_active( act ) != 0 ) {
4462 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4463 } else {
4464 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4465 }
4466 }
4468 // quit if run by the attr_changed listener
4469 if (g_object_get_data( tbl, "freeze" )) {
4470 return;
4471 }
4473 // in turn, prevent listener from responding
4474 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4476 bool modmade = false;
4478 if ( ege_select_one_action_get_active(act) != 0 ) {
4479 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4480 items != NULL;
4481 items = items->next)
4482 {
4483 if (SP_IS_ARC((SPItem *) items->data)) {
4484 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4485 repr->setAttribute("sodipodi:open", "true");
4486 SP_OBJECT((SPItem *) items->data)->updateRepr();
4487 modmade = true;
4488 }
4489 }
4490 } else {
4491 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4492 items != NULL;
4493 items = items->next)
4494 {
4495 if (SP_IS_ARC((SPItem *) items->data)) {
4496 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4497 repr->setAttribute("sodipodi:open", NULL);
4498 SP_OBJECT((SPItem *) items->data)->updateRepr();
4499 modmade = true;
4500 }
4501 }
4502 }
4504 if (modmade) {
4505 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4506 _("Arc: Change open/closed"));
4507 }
4509 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4510 }
4512 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4513 {
4514 GtkAdjustment *adj;
4515 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4516 gtk_adjustment_set_value(adj, 0.0);
4517 gtk_adjustment_value_changed(adj);
4519 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4520 gtk_adjustment_set_value(adj, 0.0);
4521 gtk_adjustment_value_changed(adj);
4523 spinbutton_defocus( GTK_OBJECT(obj) );
4524 }
4526 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4527 gchar const */*old_value*/, gchar const */*new_value*/,
4528 bool /*is_interactive*/, gpointer data)
4529 {
4530 GObject *tbl = G_OBJECT(data);
4532 // quit if run by the _changed callbacks
4533 if (g_object_get_data( tbl, "freeze" )) {
4534 return;
4535 }
4537 // in turn, prevent callbacks from responding
4538 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4540 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4541 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4543 GtkAdjustment *adj1,*adj2;
4544 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4545 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4546 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4547 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4549 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4551 char const *openstr = NULL;
4552 openstr = repr->attribute("sodipodi:open");
4553 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4555 if (openstr) {
4556 ege_select_one_action_set_active( ocb, 1 );
4557 } else {
4558 ege_select_one_action_set_active( ocb, 0 );
4559 }
4561 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4562 }
4564 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4565 NULL, /* child_added */
4566 NULL, /* child_removed */
4567 arc_tb_event_attr_changed,
4568 NULL, /* content_changed */
4569 NULL /* order_changed */
4570 };
4573 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4574 {
4575 int n_selected = 0;
4576 Inkscape::XML::Node *repr = NULL;
4578 purge_repr_listener( tbl, tbl );
4580 for (GSList const *items = selection->itemList();
4581 items != NULL;
4582 items = items->next)
4583 {
4584 if (SP_IS_ARC((SPItem *) items->data)) {
4585 n_selected++;
4586 repr = SP_OBJECT_REPR((SPItem *) items->data);
4587 }
4588 }
4590 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4592 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4593 if (n_selected == 0) {
4594 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4595 } else if (n_selected == 1) {
4596 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4597 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4599 if (repr) {
4600 g_object_set_data( tbl, "repr", repr );
4601 Inkscape::GC::anchor(repr);
4602 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4603 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4604 }
4605 } else {
4606 // FIXME: implement averaging of all parameters for multiple selected
4607 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4608 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4609 sp_arctb_sensitivize( tbl, 1, 0 );
4610 }
4611 }
4614 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4615 {
4616 EgeAdjustmentAction* eact = 0;
4617 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4620 {
4621 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4622 ege_output_action_set_use_markup( act, TRUE );
4623 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4624 g_object_set_data( holder, "mode_action", act );
4625 }
4627 /* Start */
4628 {
4629 eact = create_adjustment_action( "ArcStartAction",
4630 _("Start"), _("Start:"),
4631 _("The angle (in degrees) from the horizontal to the arc's start point"),
4632 "tools.shapes.arc", "start", 0.0,
4633 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4634 -360.0, 360.0, 1.0, 10.0,
4635 0, 0, 0,
4636 sp_arctb_start_value_changed);
4637 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4638 }
4640 /* End */
4641 {
4642 eact = create_adjustment_action( "ArcEndAction",
4643 _("End"), _("End:"),
4644 _("The angle (in degrees) from the horizontal to the arc's end point"),
4645 "tools.shapes.arc", "end", 0.0,
4646 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4647 -360.0, 360.0, 1.0, 10.0,
4648 0, 0, 0,
4649 sp_arctb_end_value_changed);
4650 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4651 }
4653 /* Segments / Pie checkbox */
4654 {
4655 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4657 GtkTreeIter iter;
4658 gtk_list_store_append( model, &iter );
4659 gtk_list_store_set( model, &iter,
4660 0, _("Closed arc"),
4661 1, _("Switch to segment (closed shape with two radii)"),
4662 2, "circle_closed_arc",
4663 -1 );
4665 gtk_list_store_append( model, &iter );
4666 gtk_list_store_set( model, &iter,
4667 0, _("Open Arc"),
4668 1, _("Switch to arc (unclosed shape)"),
4669 2, "circle_open_arc",
4670 -1 );
4672 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4673 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4674 g_object_set_data( holder, "open_action", act );
4676 ege_select_one_action_set_appearance( act, "full" );
4677 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4678 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4679 ege_select_one_action_set_icon_column( act, 2 );
4680 ege_select_one_action_set_icon_size( act, secondarySize );
4681 ege_select_one_action_set_tooltip_column( act, 1 );
4683 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4684 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4685 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4686 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4687 }
4689 /* Make Whole */
4690 {
4691 InkAction* inky = ink_action_new( "ArcResetAction",
4692 _("Make whole"),
4693 _("Make the shape a whole ellipse, not arc or segment"),
4694 "reset_circle",
4695 secondarySize );
4696 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4697 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4698 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4699 g_object_set_data( holder, "make_whole", inky );
4700 }
4702 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4703 // sensitivize make whole and open checkbox
4704 {
4705 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4706 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4707 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4708 }
4711 sigc::connection *connection = new sigc::connection(
4712 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4713 );
4714 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4715 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4716 }
4721 // toggle button callbacks and updaters
4723 //########################
4724 //## Dropper ##
4725 //########################
4727 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4728 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4729 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4730 if ( set_action ) {
4731 if ( gtk_toggle_action_get_active( act ) ) {
4732 gtk_action_set_sensitive( set_action, TRUE );
4733 } else {
4734 gtk_action_set_sensitive( set_action, FALSE );
4735 }
4736 }
4738 spinbutton_defocus(GTK_OBJECT(tbl));
4739 }
4741 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4742 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4743 spinbutton_defocus(GTK_OBJECT(tbl));
4744 }
4747 /**
4748 * Dropper auxiliary toolbar construction and setup.
4749 *
4750 * TODO: Would like to add swatch of current color.
4751 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4752 * can drag and drop places. Will provide a nice mixing palette.
4753 */
4754 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4755 {
4756 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4758 {
4759 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4760 ege_output_action_set_use_markup( act, TRUE );
4761 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4762 }
4764 {
4765 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4766 _("Pick opacity"),
4767 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4768 NULL,
4769 Inkscape::ICON_SIZE_DECORATION );
4770 g_object_set( act, "short_label", _("Pick"), NULL );
4771 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4772 g_object_set_data( holder, "pick_action", act );
4773 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4774 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4775 }
4777 {
4778 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4779 _("Assign opacity"),
4780 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4781 NULL,
4782 Inkscape::ICON_SIZE_DECORATION );
4783 g_object_set( act, "short_label", _("Assign"), NULL );
4784 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4785 g_object_set_data( holder, "set_action", act );
4786 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4787 // make sure it's disabled if we're not picking alpha
4788 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4789 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4790 }
4791 }
4794 //########################
4795 //## LPETool ##
4796 //########################
4798 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4800 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4801 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4802 {
4803 using namespace Inkscape::LivePathEffect;
4805 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4806 SPEventContext *ec = desktop->event_context;
4807 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4808 return;
4809 }
4811 // only take action if run by the attr_changed listener
4812 if (!g_object_get_data(tbl, "freeze")) {
4813 // in turn, prevent listener from responding
4814 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4816 gint mode = ege_select_one_action_get_active(act);
4817 EffectType type = lpesubtools[mode];
4819 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4820 bool success = lpetool_try_construction(lc, type);
4821 if (success) {
4822 // since the construction was already performed, we set the state back to inactive
4823 ege_select_one_action_set_active(act, 0);
4824 mode = 0;
4825 } else {
4826 // switch to the chosen subtool
4827 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4828 }
4830 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4831 prefs_set_int_attribute( "tools.lpetool", "mode", mode );
4832 }
4834 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4835 }
4836 }
4838 void
4839 sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4840 {
4841 SPEventContext *ec = selection->desktop()->event_context;
4842 if (!SP_IS_LPETOOL_CONTEXT(ec))
4843 return;
4845 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4846 }
4848 void
4849 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4850 {
4851 using namespace Inkscape::LivePathEffect;
4852 SPEventContext *ec = selection->desktop()->event_context;
4853 if (!SP_IS_LPETOOL_CONTEXT(ec))
4854 return;
4855 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4857 lpetool_delete_measuring_items(lc);
4858 lpetool_create_measuring_items(lc, selection);
4860 // activate line segment combo box if a single item with LPELineSegment is selected
4861 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4862 SPItem *item = selection->singleItem();
4863 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4864 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4865 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4866 if (lpe && lpe->effectType() == LINE_SEGMENT) {
4867 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4868 g_object_set_data(tbl, "currentlpe", lpe);
4869 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4870 gtk_action_set_sensitive(w, TRUE);
4871 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4872 } else {
4873 g_object_set_data(tbl, "currentlpe", NULL);
4874 g_object_set_data(tbl, "currentlpeitem", NULL);
4875 gtk_action_set_sensitive(w, FALSE);
4876 }
4877 } else {
4878 g_object_set_data(tbl, "currentlpe", NULL);
4879 g_object_set_data(tbl, "currentlpeitem", NULL);
4880 gtk_action_set_sensitive(w, FALSE);
4881 }
4882 }
4884 static void
4885 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4886 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4888 bool show = gtk_toggle_action_get_active( act );
4889 prefs_set_int_attribute ("tools.lpetool", "show_bbox", show ? 1 : 0);
4891 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4892 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4893 lpetool_context_reset_limiting_bbox(lc);
4894 }
4895 }
4897 static void
4898 lpetool_toggle_show_measuring_info (GtkToggleAction *act, gpointer data) {
4899 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4900 if (!tools_isactive(desktop, TOOLS_LPETOOL))
4901 return;
4903 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4904 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4905 bool show = gtk_toggle_action_get_active( act );
4906 prefs_set_int_attribute ("tools.lpetool", "show_measuring_info", show ? 1 : 0);
4907 lpetool_show_measuring_info(lc, show);
4908 }
4909 }
4911 static void
4912 lpetool_unit_changed(GtkAction* act, GObject* tbl) {
4913 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4914 SPUnit const *unit = tracker->getActiveUnit();
4915 prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
4917 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4918 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4919 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4920 lpetool_delete_measuring_items(lc);
4921 lpetool_create_measuring_items(lc);
4922 }
4923 }
4925 static void
4926 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4927 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4928 Inkscape::Selection *selection = desktop->selection;
4930 boost::optional<NR::Rect> bbox = selection->bounds();
4932 if (bbox) {
4933 Geom::Point A(bbox->min());
4934 Geom::Point B(bbox->max());
4936 A *= desktop->doc2dt();
4937 B *= desktop->doc2dt();
4939 // TODO: should we provide a way to store points in prefs?
4940 prefs_set_double_attribute ("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4941 prefs_set_double_attribute ("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4942 prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4943 prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4945 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4946 }
4948 gtk_toggle_action_set_active(act, false);
4949 }
4951 static void
4952 sp_line_segment_build_list(GObject *tbl)
4953 {
4954 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
4956 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
4957 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4958 gtk_list_store_clear (model);
4960 // TODO: we add the entries of rht combo box manually; later this should be done automatically
4961 {
4962 GtkTreeIter iter;
4963 gtk_list_store_append( model, &iter );
4964 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
4965 gtk_list_store_append( model, &iter );
4966 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
4967 gtk_list_store_append( model, &iter );
4968 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
4969 gtk_list_store_append( model, &iter );
4970 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
4971 }
4973 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
4974 }
4976 static void
4977 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
4978 using namespace Inkscape::LivePathEffect;
4980 // quit if run by the attr_changed listener
4981 if (g_object_get_data(tbl, "freeze")) {
4982 return;
4983 }
4985 // in turn, prevent listener from responding
4986 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4988 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
4989 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4990 if (lpeitem) {
4991 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4992 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
4993 sp_lpe_item_update_patheffect(lpeitem, true, true);
4994 }
4996 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4997 }
4999 static void
5000 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5001 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5003 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5004 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5005 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5006 }
5007 gtk_toggle_action_set_active(act, false);
5008 }
5010 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5011 {
5012 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5013 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5014 g_object_set_data(holder, "tracker", tracker);
5015 SPUnit const *unit = tracker->getActiveUnit();
5016 prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
5018 /** Automatically create a list of LPEs that get added to the toolbar **/
5019 {
5020 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5022 GtkTreeIter iter;
5024 // the first toggle button represents the state that no subtool is active (remove this when
5025 // this can be modeled by EgeSelectOneAction or some other action)
5026 gtk_list_store_append( model, &iter );
5027 gtk_list_store_set( model, &iter,
5028 0, _("All inactive"),
5029 1, _("No geometric tool is active"),
5030 2, _("all_inactive"),
5031 -1 );
5033 Inkscape::LivePathEffect::EffectType type;
5034 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5035 type = lpesubtools[i];
5036 gtk_list_store_append( model, &iter );
5037 gtk_list_store_set( model, &iter,
5038 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5039 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5040 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5041 -1 );
5042 }
5044 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5045 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5046 g_object_set_data( holder, "lpetool_mode_action", act );
5048 ege_select_one_action_set_appearance( act, "full" );
5049 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5050 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5051 ege_select_one_action_set_icon_column( act, 2 );
5052 ege_select_one_action_set_tooltip_column( act, 1 );
5054 gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
5055 ege_select_one_action_set_active( act, lpeToolMode );
5056 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5057 }
5059 /* Show limiting bounding box */
5060 {
5061 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5062 _("Show limiting bounding box"),
5063 _("Show bounding box (used to cut infinite lines)"),
5064 "lpetool_show_bbox",
5065 Inkscape::ICON_SIZE_DECORATION );
5066 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5067 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5068 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
5069 }
5071 /* Set limiting bounding box to bbox of current selection */
5072 {
5073 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5074 _("Get limiting bounding box from selection"),
5075 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5076 "lpetool_set_bbox",
5077 Inkscape::ICON_SIZE_DECORATION );
5078 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5079 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5080 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5081 }
5084 /* Combo box to choose line segment type */
5085 {
5086 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5087 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5088 ege_select_one_action_set_appearance (act, "compact");
5089 g_object_set_data (holder, "lpetool_line_segment_action", act );
5091 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5093 sp_line_segment_build_list (holder);
5095 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5096 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5097 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5098 }
5100 /* Display measuring info for selected items */
5101 {
5102 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5103 _("Display measuring info"),
5104 _("Display measuring info for selected items"),
5105 "lpetool_measuring_info",
5106 Inkscape::ICON_SIZE_DECORATION );
5107 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5108 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), desktop );
5109 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_measuring_info", 1 ) );
5110 }
5112 // add the units menu
5113 {
5114 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5115 gtk_action_group_add_action( mainActions, act );
5116 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5117 }
5119 /* Open LPE dialog (to adapt parameters numerically) */
5120 {
5121 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5122 _("Open LPE dialog"),
5123 _("Open LPE dialog (to adapt parameters numerically)"),
5124 "lpetool_open_lpe_dialog",
5125 Inkscape::ICON_SIZE_DECORATION );
5126 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5127 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5128 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5129 }
5131 //watch selection
5132 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5134 sigc::connection *c_selection_modified =
5135 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5136 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5137 pool->add_connection ("selection-modified", c_selection_modified);
5139 sigc::connection *c_selection_changed =
5140 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5141 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5142 pool->add_connection ("selection-changed", c_selection_changed);
5143 }
5145 //########################
5146 //## Eraser ##
5147 //########################
5149 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5150 {
5151 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5152 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5153 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5154 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
5155 }
5157 // only take action if run by the attr_changed listener
5158 if (!g_object_get_data( tbl, "freeze" )) {
5159 // in turn, prevent listener from responding
5160 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5162 if ( eraserMode != 0 ) {
5163 } else {
5164 }
5165 // TODO finish implementation
5167 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5168 }
5169 }
5171 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5172 {
5173 {
5174 /* Width */
5175 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5176 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5177 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5178 _("Pen Width"), _("Width:"),
5179 _("The width of the eraser pen (relative to the visible canvas area)"),
5180 "tools.eraser", "width", 15,
5181 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5182 1, 100, 1.0, 0.0,
5183 labels, values, G_N_ELEMENTS(labels),
5184 sp_ddc_width_value_changed, 0.01, 0, 100 );
5185 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5186 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5187 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5188 }
5190 {
5191 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5193 GtkTreeIter iter;
5194 gtk_list_store_append( model, &iter );
5195 gtk_list_store_set( model, &iter,
5196 0, _("Delete"),
5197 1, _("Delete objects touched by the eraser"),
5198 2, "delete_object",
5199 -1 );
5201 gtk_list_store_append( model, &iter );
5202 gtk_list_store_set( model, &iter,
5203 0, _("Cut"),
5204 1, _("Cut out from objects"),
5205 2, "difference",
5206 -1 );
5208 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5209 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5210 g_object_set_data( holder, "eraser_mode_action", act );
5212 ege_select_one_action_set_appearance( act, "full" );
5213 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5214 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5215 ege_select_one_action_set_icon_column( act, 2 );
5216 ege_select_one_action_set_tooltip_column( act, 1 );
5218 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5219 ege_select_one_action_set_active( act, eraserMode );
5220 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5221 }
5223 }
5225 //########################
5226 //## Text Toolbox ##
5227 //########################
5228 /*
5229 static void
5230 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5231 {
5232 //Call back for letter sizing spinbutton
5233 }
5235 static void
5236 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5237 {
5238 //Call back for line height spinbutton
5239 }
5241 static void
5242 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5243 {
5244 //Call back for horizontal kerning spinbutton
5245 }
5247 static void
5248 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5249 {
5250 //Call back for vertical kerning spinbutton
5251 }
5253 static void
5254 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5255 {
5256 //Call back for letter rotation spinbutton
5257 }*/
5259 namespace {
5261 bool popdown_visible = false;
5262 bool popdown_hasfocus = false;
5264 void
5265 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5266 {
5267 SPStyle *query =
5268 sp_style_new (SP_ACTIVE_DOCUMENT);
5270 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5272 int result_family =
5273 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5275 int result_style =
5276 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5278 int result_numbers =
5279 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5281 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5283 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5284 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5285 // there are no texts in selection, read from prefs
5287 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5288 if (repr) {
5289 sp_style_read_from_repr (query, repr);
5290 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5291 // do not reset the toolbar style from prefs if we already did it last time
5292 sp_style_unref(query);
5293 return;
5294 }
5295 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5296 } else {
5297 sp_style_unref(query);
5298 return;
5299 }
5300 } else {
5301 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5302 }
5304 if (query->text)
5305 {
5306 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5307 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5308 gtk_entry_set_text (GTK_ENTRY (entry), "");
5310 } else if (query->text->font_specification.value || query->text->font_family.value) {
5312 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5314 // Get the font that corresponds
5315 Glib::ustring familyName;
5317 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5318 if (font) {
5319 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5320 font->Unref();
5321 font = NULL;
5322 }
5324 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5326 Gtk::TreePath path;
5327 try {
5328 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5329 } catch (...) {
5330 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5331 sp_style_unref(query);
5332 return;
5333 }
5335 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5336 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5338 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5340 gtk_tree_selection_select_path (tselection, path.gobj());
5341 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5343 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5344 }
5346 //Size
5347 {
5348 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5349 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5350 g_object_set_data(tbl, "size-block", gpointer(1));
5351 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5352 g_object_set_data(tbl, "size-block", gpointer(0));
5353 g_free(str);
5354 }
5356 //Anchor
5357 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5358 {
5359 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5360 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5361 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5362 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5363 }
5364 else
5365 {
5366 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5367 {
5368 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5369 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5370 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5371 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5372 }
5373 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5374 {
5375 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5376 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5377 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5378 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5379 }
5380 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5381 {
5382 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5383 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5384 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5385 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5386 }
5387 }
5389 //Style
5390 {
5391 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5393 gboolean active = gtk_toggle_button_get_active (button);
5394 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5396 if (active != check)
5397 {
5398 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5399 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5400 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5401 }
5402 }
5404 {
5405 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5407 gboolean active = gtk_toggle_button_get_active (button);
5408 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5410 if (active != check)
5411 {
5412 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5413 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5414 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5415 }
5416 }
5418 //Orientation
5419 //locking both buttons, changing one affect all group (both)
5420 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5421 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5423 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5424 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5426 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5427 {
5428 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5429 }
5430 else
5431 {
5432 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5433 }
5434 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5435 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5436 }
5438 sp_style_unref(query);
5439 }
5441 void
5442 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5443 {
5444 sp_text_toolbox_selection_changed (selection, tbl);
5445 }
5447 void
5448 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5449 {
5450 sp_text_toolbox_selection_changed (NULL, tbl);
5451 }
5453 void
5454 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5455 GObject *tbl)
5456 {
5457 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5458 GtkTreeModel *model = 0;
5459 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5460 GtkTreeIter iter;
5461 char *family = 0;
5463 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5464 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5466 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5467 return;
5468 }
5470 gtk_tree_model_get (model, &iter, 0, &family, -1);
5472 if (g_object_get_data (G_OBJECT (selection), "block"))
5473 {
5474 gtk_entry_set_text (GTK_ENTRY (entry), family);
5475 return;
5476 }
5478 gtk_entry_set_text (GTK_ENTRY (entry), family);
5480 SPStyle *query =
5481 sp_style_new (SP_ACTIVE_DOCUMENT);
5483 int result_fontspec =
5484 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5486 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5488 SPCSSAttr *css = sp_repr_css_attr_new ();
5491 // First try to get the font spec from the stored value
5492 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5494 if (fontSpec.empty()) {
5495 // Construct a new font specification if it does not yet exist
5496 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5497 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5498 fontFromStyle->Unref();
5499 }
5501 if (!fontSpec.empty()) {
5502 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5503 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5504 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5505 if (font) {
5506 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5508 // Set all the these just in case they were altered when finding the best
5509 // match for the new family and old style...
5511 gchar c[256];
5513 font->Family(c, 256);
5514 sp_repr_css_set_property (css, "font-family", c);
5516 font->Attribute( "weight", c, 256);
5517 sp_repr_css_set_property (css, "font-weight", c);
5519 font->Attribute("style", c, 256);
5520 sp_repr_css_set_property (css, "font-style", c);
5522 font->Attribute("stretch", c, 256);
5523 sp_repr_css_set_property (css, "font-stretch", c);
5525 font->Attribute("variant", c, 256);
5526 sp_repr_css_set_property (css, "font-variant", c);
5528 font->Unref();
5529 }
5530 }
5531 }
5533 // If querying returned nothing, set the default style of the tool (for new texts)
5534 if (result_fontspec == QUERY_STYLE_NOTHING)
5535 {
5536 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5537 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5538 }
5539 else
5540 {
5541 sp_desktop_set_style (desktop, css, true, true);
5542 }
5544 sp_style_unref(query);
5546 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5547 _("Text: Change font family"));
5548 sp_repr_css_attr_unref (css);
5549 g_free(family);
5550 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5552 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5553 }
5555 /* This is where execution comes when the contents of the font family box have been completed
5556 by the press of the return key */
5557 void
5558 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5559 GObject *tbl)
5560 {
5561 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5563 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5564 try {
5565 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5566 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5567 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5568 gtk_tree_selection_select_path (selection, path.gobj());
5569 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5570 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5571 } catch (...) {
5572 if (family && strlen (family))
5573 {
5574 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5575 }
5576 }
5577 }
5579 void
5580 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5581 gpointer data)
5582 {
5583 if (g_object_get_data (G_OBJECT (button), "block")) return;
5584 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5585 int prop = GPOINTER_TO_INT(data);
5587 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5588 SPCSSAttr *css = sp_repr_css_attr_new ();
5590 switch (prop)
5591 {
5592 case 0:
5593 {
5594 sp_repr_css_set_property (css, "text-anchor", "start");
5595 sp_repr_css_set_property (css, "text-align", "start");
5596 break;
5597 }
5598 case 1:
5599 {
5600 sp_repr_css_set_property (css, "text-anchor", "middle");
5601 sp_repr_css_set_property (css, "text-align", "center");
5602 break;
5603 }
5605 case 2:
5606 {
5607 sp_repr_css_set_property (css, "text-anchor", "end");
5608 sp_repr_css_set_property (css, "text-align", "end");
5609 break;
5610 }
5612 case 3:
5613 {
5614 sp_repr_css_set_property (css, "text-anchor", "start");
5615 sp_repr_css_set_property (css, "text-align", "justify");
5616 break;
5617 }
5618 }
5620 SPStyle *query =
5621 sp_style_new (SP_ACTIVE_DOCUMENT);
5622 int result_numbers =
5623 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5625 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5626 if (result_numbers == QUERY_STYLE_NOTHING)
5627 {
5628 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5629 }
5631 sp_style_unref(query);
5633 sp_desktop_set_style (desktop, css, true, true);
5634 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5635 _("Text: Change alignment"));
5636 sp_repr_css_attr_unref (css);
5638 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5639 }
5641 void
5642 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5643 gpointer data)
5644 {
5645 if (g_object_get_data (G_OBJECT (button), "block")) return;
5647 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5648 SPCSSAttr *css = sp_repr_css_attr_new ();
5649 int prop = GPOINTER_TO_INT(data);
5650 bool active = gtk_toggle_button_get_active (button);
5652 SPStyle *query =
5653 sp_style_new (SP_ACTIVE_DOCUMENT);
5655 int result_fontspec =
5656 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5658 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5659 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5660 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5662 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5663 Glib::ustring newFontSpec = "";
5665 if (fontSpec.empty()) {
5666 // Construct a new font specification if it does not yet exist
5667 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5668 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5669 fontFromStyle->Unref();
5670 }
5672 switch (prop)
5673 {
5674 case 0:
5675 {
5676 if (!fontSpec.empty()) {
5677 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5678 }
5679 if (fontSpec != newFontSpec) {
5680 // Don't even set the bold if the font didn't exist on the system
5681 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5682 }
5683 break;
5684 }
5686 case 1:
5687 {
5688 if (!fontSpec.empty()) {
5689 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5690 }
5691 if (fontSpec != newFontSpec) {
5692 // Don't even set the italic if the font didn't exist on the system
5693 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5694 }
5695 break;
5696 }
5697 }
5699 if (!newFontSpec.empty()) {
5700 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5701 }
5703 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5704 if (result_fontspec == QUERY_STYLE_NOTHING)
5705 {
5706 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5707 }
5709 sp_style_unref(query);
5711 sp_desktop_set_style (desktop, css, true, true);
5712 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5713 _("Text: Change font style"));
5714 sp_repr_css_attr_unref (css);
5716 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5717 }
5719 void
5720 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5721 gpointer data)
5722 {
5723 if (g_object_get_data (G_OBJECT (button), "block")) {
5724 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5725 return;
5726 }
5728 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5729 SPCSSAttr *css = sp_repr_css_attr_new ();
5730 int prop = GPOINTER_TO_INT(data);
5732 switch (prop)
5733 {
5734 case 0:
5735 {
5736 sp_repr_css_set_property (css, "writing-mode", "lr");
5737 break;
5738 }
5740 case 1:
5741 {
5742 sp_repr_css_set_property (css, "writing-mode", "tb");
5743 break;
5744 }
5745 }
5747 SPStyle *query =
5748 sp_style_new (SP_ACTIVE_DOCUMENT);
5749 int result_numbers =
5750 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5752 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5753 if (result_numbers == QUERY_STYLE_NOTHING)
5754 {
5755 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5756 }
5758 sp_desktop_set_style (desktop, css, true, true);
5759 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5760 _("Text: Change orientation"));
5761 sp_repr_css_attr_unref (css);
5763 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5764 }
5766 gboolean
5767 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5768 {
5769 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5770 if (!desktop) return FALSE;
5772 switch (get_group0_keyval (event)) {
5773 case GDK_Escape: // defocus
5774 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5775 sp_text_toolbox_selection_changed (NULL, tbl); // update
5776 return TRUE; // I consumed the event
5777 break;
5778 }
5779 return FALSE;
5780 }
5782 gboolean
5783 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5784 {
5785 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5786 if (!desktop) return FALSE;
5788 switch (get_group0_keyval (event)) {
5789 case GDK_KP_Enter:
5790 case GDK_Return:
5791 case GDK_Escape: // defocus
5792 gtk_widget_hide (w);
5793 popdown_visible = false;
5794 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5795 return TRUE; // I consumed the event
5796 break;
5797 case GDK_w:
5798 case GDK_W:
5799 if (event->state & GDK_CONTROL_MASK) {
5800 gtk_widget_hide (w);
5801 popdown_visible = false;
5802 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5803 return TRUE; // I consumed the event
5804 }
5805 break;
5806 }
5807 return FALSE;
5808 }
5811 void
5812 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5813 GObject *tbl)
5814 {
5815 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5817 if (g_object_get_data (tbl, "size-block")) return;
5819 // If this is not from selecting a size in the list (in which case get_active will give the
5820 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5821 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5822 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5823 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5824 return;
5826 gdouble value = -1;
5827 {
5828 gchar *endptr;
5829 gchar *const text = gtk_combo_box_get_active_text(cbox);
5830 if (text) {
5831 value = g_strtod(text, &endptr);
5832 if (endptr == text) { // Conversion failed, non-numeric input.
5833 value = -1;
5834 }
5835 g_free(text);
5836 }
5837 }
5838 if (value <= 0) {
5839 return; // could not parse value
5840 }
5842 SPCSSAttr *css = sp_repr_css_attr_new ();
5843 Inkscape::CSSOStringStream osfs;
5844 osfs << value;
5845 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5847 SPStyle *query =
5848 sp_style_new (SP_ACTIVE_DOCUMENT);
5849 int result_numbers =
5850 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5852 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5853 if (result_numbers == QUERY_STYLE_NOTHING)
5854 {
5855 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5856 }
5858 sp_style_unref(query);
5860 sp_desktop_set_style (desktop, css, true, true);
5861 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5862 _("Text: Change font size"));
5863 sp_repr_css_attr_unref (css);
5865 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5866 }
5868 gboolean
5869 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5870 {
5871 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5872 if (!desktop) return FALSE;
5874 if (!g_object_get_data (tbl, "esc-pressed")) {
5875 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5876 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5877 sp_text_toolbox_size_changed (cbox, tbl);
5878 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5879 }
5880 return FALSE; // I consumed the event
5881 }
5884 gboolean
5885 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5886 {
5887 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5888 if (!desktop) return FALSE;
5890 switch (get_group0_keyval (event)) {
5891 case GDK_Escape: // defocus
5892 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5893 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5894 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5895 return TRUE; // I consumed the event
5896 break;
5897 case GDK_Return: // defocus
5898 case GDK_KP_Enter:
5899 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5900 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5901 sp_text_toolbox_size_changed (cbox, tbl);
5902 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5903 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5904 return TRUE; // I consumed the event
5905 break;
5906 }
5907 return FALSE;
5908 }
5910 void
5911 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5912 GObject *tbl)
5913 {
5914 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5915 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5916 int x, y;
5918 if (!popdown_visible)
5919 {
5920 gdk_window_get_origin (widget->window, &x, &y);
5921 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5922 gtk_widget_show_all (popdown);
5923 //sp_transientize (popdown);
5925 gdk_pointer_grab (widget->window, TRUE,
5926 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5927 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5928 GDK_POINTER_MOTION_MASK),
5929 NULL, NULL, GDK_CURRENT_TIME);
5931 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5933 popdown_visible = true;
5934 }
5935 else
5936 {
5937 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5938 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5939 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5940 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5941 gtk_widget_hide (popdown);
5942 popdown_visible = false;
5943 }
5944 }
5946 gboolean
5947 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5948 GdkEventFocus */*event*/,
5949 GObject */*tbl*/)
5950 {
5951 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5952 return FALSE;
5953 }
5955 gboolean
5956 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5957 GdkEventFocus */*event*/,
5958 GObject */*tbl*/)
5959 {
5960 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5962 if (popdown_hasfocus) {
5963 gtk_widget_hide (popdown);
5964 popdown_hasfocus = false;
5965 popdown_visible = false;
5966 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5967 return TRUE;
5968 }
5969 return FALSE;
5970 }
5972 gboolean
5973 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5974 GdkEventFocus */*event*/,
5975 GObject */*tbl*/)
5976 {
5977 popdown_hasfocus = true;
5978 return TRUE;
5979 }
5982 void
5983 cell_data_func (GtkTreeViewColumn */*column*/,
5984 GtkCellRenderer *cell,
5985 GtkTreeModel *tree_model,
5986 GtkTreeIter *iter,
5987 gpointer /*data*/)
5988 {
5989 gchar *family;
5990 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5991 gchar *const family_escaped = g_markup_escape_text(family, -1);
5993 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5994 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5996 std::stringstream markup;
5997 markup << family_escaped << " <span foreground='darkgray' font_family='"
5998 << family_escaped << "'>" << sample_escaped << "</span>";
5999 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6001 g_free(family);
6002 g_free(family_escaped);
6003 g_free(sample_escaped);
6004 }
6006 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6007 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6008 if (completion) {
6009 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6010 g_object_unref (completion);
6011 }
6012 }
6014 GtkWidget*
6015 sp_text_toolbox_new (SPDesktop *desktop)
6016 {
6017 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6018 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
6020 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6021 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6023 GtkTooltips *tt = gtk_tooltips_new();
6024 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6026 ////////////Family
6027 //Window
6028 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6029 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6031 //Entry
6032 GtkWidget *entry = gtk_entry_new ();
6033 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6034 GtkEntryCompletion *completion = gtk_entry_completion_new ();
6035 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6036 gtk_entry_completion_set_text_column (completion, 0);
6037 gtk_entry_completion_set_minimum_key_length (completion, 1);
6038 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6039 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6040 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6041 gtk_toolbar_append_widget( tbl, entry, "", "" );
6042 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6044 //Button
6045 GtkWidget *button = gtk_button_new ();
6046 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6047 gtk_toolbar_append_widget( tbl, button, "", "");
6049 //Popdown
6050 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
6051 GtkWidget *treeview = gtk_tree_view_new ();
6053 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6054 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
6055 gtk_tree_view_column_pack_start (column, cell, FALSE);
6056 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6057 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6058 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6060 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6061 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6062 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6064 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6066 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6067 gtk_container_add (GTK_CONTAINER (sw), treeview);
6069 gtk_container_add (GTK_CONTAINER (window), sw);
6070 gtk_widget_set_size_request (window, 300, 450);
6072 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6073 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6074 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6076 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6078 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6079 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6080 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6082 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6083 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6085 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6086 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6087 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6088 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6089 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6091 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6092 GtkWidget *box = gtk_event_box_new ();
6093 gtk_container_add (GTK_CONTAINER (box), image);
6094 gtk_toolbar_append_widget( tbl, box, "", "");
6095 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6096 GtkTooltips *tooltips = gtk_tooltips_new ();
6097 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6098 gtk_widget_hide (GTK_WIDGET (box));
6099 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6101 ////////////Size
6102 gchar const *const sizes[] = {
6103 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6104 "16", "18", "20", "22", "24", "28",
6105 "32", "36", "40", "48", "56", "64", "72", "144"
6106 };
6108 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6109 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6110 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6111 }
6112 gtk_widget_set_size_request (cbox, 80, -1);
6113 gtk_toolbar_append_widget( tbl, cbox, "", "");
6114 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6115 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6116 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6117 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6119 ////////////Text anchor
6120 GtkWidget *group = gtk_radio_button_new (NULL);
6121 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6122 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6124 // left
6125 GtkWidget *rbutton = group;
6126 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6127 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6128 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6130 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6131 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6132 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6133 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6135 // center
6136 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6137 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6138 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6139 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6141 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6142 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6143 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6144 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6146 // right
6147 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6148 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6149 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6150 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6152 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6153 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6154 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6155 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6157 // fill
6158 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6159 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6160 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6161 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6163 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6164 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6165 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6166 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6168 gtk_toolbar_append_widget( tbl, row, "", "");
6170 //spacer
6171 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6173 ////////////Text style
6174 row = gtk_hbox_new (FALSE, 4);
6176 // bold
6177 rbutton = gtk_toggle_button_new ();
6178 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6179 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6180 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6181 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6183 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6184 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6185 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6187 // italic
6188 rbutton = gtk_toggle_button_new ();
6189 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6190 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6191 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6192 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6194 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6195 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6196 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6198 gtk_toolbar_append_widget( tbl, row, "", "");
6200 //spacer
6201 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6203 ////////////Text orientation
6204 group = gtk_radio_button_new (NULL);
6205 row = gtk_hbox_new (FALSE, 4);
6206 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6208 // horizontal
6209 rbutton = group;
6210 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6211 gtk_container_add (GTK_CONTAINER (rbutton),
6212 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6213 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6214 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6216 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6217 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6218 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6220 // vertical
6221 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6222 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6223 gtk_container_add (GTK_CONTAINER (rbutton),
6224 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6225 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6226 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6228 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6229 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6230 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6231 gtk_toolbar_append_widget( tbl, row, "", "" );
6234 //watch selection
6235 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6237 sigc::connection *c_selection_changed =
6238 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6239 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6240 pool->add_connection ("selection-changed", c_selection_changed);
6242 sigc::connection *c_selection_modified =
6243 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6244 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6245 pool->add_connection ("selection-modified", c_selection_modified);
6247 sigc::connection *c_subselection_changed =
6248 new sigc::connection (desktop->connectToolSubselectionChanged
6249 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6250 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6252 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6255 gtk_widget_show_all( GTK_WIDGET(tbl) );
6257 return GTK_WIDGET(tbl);
6258 } // end of sp_text_toolbox_new()
6260 }//<unnamed> namespace
6263 //#########################
6264 //## Connector ##
6265 //#########################
6267 static void sp_connector_path_set_avoid(void)
6268 {
6269 cc_selection_set_avoid(true);
6270 }
6273 static void sp_connector_path_set_ignore(void)
6274 {
6275 cc_selection_set_avoid(false);
6276 }
6280 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6281 {
6282 // quit if run by the _changed callbacks
6283 if (g_object_get_data( tbl, "freeze" )) {
6284 return;
6285 }
6287 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6288 SPDocument *doc = sp_desktop_document(desktop);
6290 if (!sp_document_get_undo_sensitive(doc))
6291 {
6292 return;
6293 }
6295 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6297 if ( repr->attribute("inkscape:connector-spacing") ) {
6298 gdouble priorValue = gtk_adjustment_get_value(adj);
6299 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6300 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6301 return;
6302 }
6303 } else if ( adj->value == defaultConnSpacing ) {
6304 return;
6305 }
6307 // in turn, prevent callbacks from responding
6308 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6310 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6311 SP_OBJECT(desktop->namedview)->updateRepr();
6313 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6314 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6315 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6316 NR::Matrix m = NR::identity();
6317 avoid_item_move(&m, item);
6318 }
6320 if (items) {
6321 g_slist_free(items);
6322 }
6324 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6325 _("Change connector spacing"));
6327 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6329 spinbutton_defocus(GTK_OBJECT(tbl));
6330 }
6332 static void sp_connector_graph_layout(void)
6333 {
6334 if (!SP_ACTIVE_DESKTOP) return;
6336 // hack for clones, see comment in align-and-distribute.cpp
6337 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6338 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6340 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6342 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6344 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6345 }
6347 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6348 {
6349 if ( gtk_toggle_action_get_active( act ) ) {
6350 prefs_set_string_attribute("tools.connector", "directedlayout",
6351 "true");
6352 } else {
6353 prefs_set_string_attribute("tools.connector", "directedlayout",
6354 "false");
6355 }
6356 }
6358 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6359 {
6360 if ( gtk_toggle_action_get_active( act ) ) {
6361 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6362 "true");
6363 } else {
6364 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6365 "false");
6366 }
6367 }
6370 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6371 {
6372 prefs_set_double_attribute("tools.connector", "length", adj->value);
6373 spinbutton_defocus(GTK_OBJECT(tbl));
6374 }
6376 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6377 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6378 bool /*is_interactive*/, gpointer data)
6379 {
6380 GtkWidget *tbl = GTK_WIDGET(data);
6382 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6383 return;
6384 }
6385 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6386 return;
6387 }
6389 GtkAdjustment *adj = (GtkAdjustment*)
6390 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6391 gdouble spacing = defaultConnSpacing;
6392 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6394 gtk_adjustment_set_value(adj, spacing);
6395 }
6398 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6399 NULL, /* child_added */
6400 NULL, /* child_removed */
6401 connector_tb_event_attr_changed,
6402 NULL, /* content_changed */
6403 NULL /* order_changed */
6404 };
6407 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6408 {
6409 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6411 {
6412 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6413 _("Avoid"),
6414 _("Make connectors avoid selected objects"),
6415 "connector_avoid",
6416 secondarySize );
6417 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6418 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6419 }
6421 {
6422 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6423 _("Ignore"),
6424 _("Make connectors ignore selected objects"),
6425 "connector_ignore",
6426 secondarySize );
6427 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6428 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6429 }
6431 EgeAdjustmentAction* eact = 0;
6433 // Spacing spinbox
6434 eact = create_adjustment_action( "ConnectorSpacingAction",
6435 _("Connector Spacing"), _("Spacing:"),
6436 _("The amount of space left around objects by auto-routing connectors"),
6437 "tools.connector", "spacing", defaultConnSpacing,
6438 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6439 0, 100, 1.0, 10.0,
6440 0, 0, 0,
6441 connector_spacing_changed, 1, 0 );
6442 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6444 // Graph (connector network) layout
6445 {
6446 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6447 _("Graph"),
6448 _("Nicely arrange selected connector network"),
6449 "graph_layout",
6450 secondarySize );
6451 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6452 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6453 }
6455 // Default connector length spinbox
6456 eact = create_adjustment_action( "ConnectorLengthAction",
6457 _("Connector Length"), _("Length:"),
6458 _("Ideal length for connectors when layout is applied"),
6459 "tools.connector", "length", 100,
6460 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6461 10, 1000, 10.0, 100.0,
6462 0, 0, 0,
6463 connector_length_changed, 1, 0 );
6464 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6467 // Directed edges toggle button
6468 {
6469 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6470 _("Downwards"),
6471 _("Make connectors with end-markers (arrows) point downwards"),
6472 "directed_graph",
6473 Inkscape::ICON_SIZE_DECORATION );
6474 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6476 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6477 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6478 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6480 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6481 }
6483 // Avoid overlaps toggle button
6484 {
6485 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6486 _("Remove overlaps"),
6487 _("Do not allow overlapping shapes"),
6488 "remove_overlaps",
6489 Inkscape::ICON_SIZE_DECORATION );
6490 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6492 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6493 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6494 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6496 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6497 }
6499 // Code to watch for changes to the connector-spacing attribute in
6500 // the XML.
6501 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6502 g_assert(repr != NULL);
6504 purge_repr_listener( holder, holder );
6506 if (repr) {
6507 g_object_set_data( holder, "repr", repr );
6508 Inkscape::GC::anchor(repr);
6509 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6510 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6511 }
6512 } // end of sp_connector_toolbox_prep()
6515 //#########################
6516 //## Paintbucket ##
6517 //#########################
6519 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6520 {
6521 gint channels = ege_select_one_action_get_active( act );
6522 flood_channels_set_channels( channels );
6523 }
6525 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6526 {
6527 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6528 }
6530 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6531 {
6532 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6533 }
6535 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6536 {
6537 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6538 SPUnit const *unit = tracker->getActiveUnit();
6540 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6542 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6543 }
6545 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6546 {
6547 // FIXME: make defaults settable via Inkscape Options
6548 struct KeyValue {
6549 char const *key;
6550 double value;
6551 } const key_values[] = {
6552 {"threshold", 15},
6553 {"offset", 0.0}
6554 };
6556 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6557 KeyValue const &kv = key_values[i];
6558 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6559 if ( adj ) {
6560 gtk_adjustment_set_value(adj, kv.value);
6561 }
6562 }
6564 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6565 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6566 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6567 ege_select_one_action_set_active( autogap_action, 0 );
6568 }
6570 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6571 {
6572 EgeAdjustmentAction* eact = 0;
6574 {
6575 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6577 GList* items = 0;
6578 gint count = 0;
6579 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6580 {
6581 GtkTreeIter iter;
6582 gtk_list_store_append( model, &iter );
6583 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6584 count++;
6585 }
6586 g_list_free( items );
6587 items = 0;
6588 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6589 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6590 ege_select_one_action_set_appearance( act1, "compact" );
6591 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6592 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6593 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6594 g_object_set_data( holder, "channels_action", act1 );
6595 }
6597 // Spacing spinbox
6598 {
6599 eact = create_adjustment_action(
6600 "ThresholdAction",
6601 _("Fill Threshold"), _("Threshold:"),
6602 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6603 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6604 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6605 0, 0, 0,
6606 paintbucket_threshold_changed, 1, 0 );
6608 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6609 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6610 }
6612 // Create the units menu.
6613 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6614 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6615 if (stored_unit)
6616 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6617 g_object_set_data( holder, "tracker", tracker );
6618 {
6619 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6620 gtk_action_group_add_action( mainActions, act );
6621 }
6623 // Offset spinbox
6624 {
6625 eact = create_adjustment_action(
6626 "OffsetAction",
6627 _("Grow/shrink by"), _("Grow/shrink by:"),
6628 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6629 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6630 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6631 0, 0, 0,
6632 paintbucket_offset_changed, 1, 2);
6633 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6635 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6636 }
6638 /* Auto Gap */
6639 {
6640 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6642 GList* items = 0;
6643 gint count = 0;
6644 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6645 {
6646 GtkTreeIter iter;
6647 gtk_list_store_append( model, &iter );
6648 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6649 count++;
6650 }
6651 g_list_free( items );
6652 items = 0;
6653 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6654 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6655 ege_select_one_action_set_appearance( act2, "compact" );
6656 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6657 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6658 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6659 g_object_set_data( holder, "autogap_action", act2 );
6660 }
6662 /* Reset */
6663 {
6664 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6665 _("Defaults"),
6666 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6667 GTK_STOCK_CLEAR );
6668 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6669 gtk_action_group_add_action( mainActions, act );
6670 gtk_action_set_sensitive( act, TRUE );
6671 }
6673 }
6675 /*
6676 Local Variables:
6677 mode:c++
6678 c-file-style:"stroustrup"
6679 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6680 indent-tabs-mode:nil
6681 fill-column:99
6682 End:
6683 */
6684 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :