1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 * Maximilian Albert <maximilian.albert@gmail.com>
18 *
19 * Copyright (C) 2004 David Turner
20 * Copyright (C) 2003 MenTaLguY
21 * Copyright (C) 1999-2008 authors
22 * Copyright (C) 2001-2002 Ximian, Inc.
23 *
24 * Released under GNU GPL, read the file 'COPYING' for more information
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <cstring>
32 #include <string>
34 #include <gtkmm.h>
35 #include <gtk/gtk.h>
36 #include <iostream>
37 #include <sstream>
39 #include "widgets/button.h"
40 #include "widgets/widget-sizes.h"
41 #include "widgets/spw-utilities.h"
42 #include "widgets/spinbutton-events.h"
43 #include "dialogs/text-edit.h"
44 #include "dialogs/dialog-events.h"
46 #include "ui/widget/style-swatch.h"
48 #include "prefs-utils.h"
49 #include "verbs.h"
50 #include "sp-namedview.h"
51 #include "desktop.h"
52 #include "desktop-handles.h"
53 #include "xml/repr.h"
54 #include "xml/node-event-vector.h"
55 #include "xml/attribute-record.h"
56 #include <glibmm/i18n.h>
57 #include "helper/unit-menu.h"
58 #include "helper/units.h"
59 #include "live_effects/effect.h"
61 #include "inkscape.h"
62 #include "conn-avoid-ref.h"
65 #include "select-toolbar.h"
66 #include "gradient-toolbar.h"
68 #include "connector-context.h"
69 #include "node-context.h"
70 #include "pen-context.h"
71 #include "lpe-tool-context.h"
72 #include "shape-editor.h"
73 #include "tweak-context.h"
74 #include "sp-rect.h"
75 #include "box3d.h"
76 #include "box3d-context.h"
77 #include "sp-star.h"
78 #include "sp-spiral.h"
79 #include "sp-ellipse.h"
80 #include "sp-text.h"
81 #include "sp-flowtext.h"
82 #include "sp-clippath.h"
83 #include "sp-mask.h"
84 #include "style.h"
85 #include "tools-switch.h"
86 #include "selection.h"
87 #include "selection-chemistry.h"
88 #include "document-private.h"
89 #include "desktop-style.h"
90 #include "../libnrtype/font-lister.h"
91 #include "../libnrtype/font-instance.h"
92 #include "../connection-pool.h"
93 #include "../prefs-utils.h"
94 #include "../inkscape-stock.h"
95 #include "icon.h"
96 #include "graphlayout/graphlayout.h"
97 #include "interface.h"
98 #include "shortcuts.h"
100 #include "mod360.h"
102 #include "toolbox.h"
104 #include "flood-context.h"
106 #include "ink-action.h"
107 #include "ege-adjustment-action.h"
108 #include "ege-output-action.h"
109 #include "ege-select-one-action.h"
110 #include "helper/unit-tracker.h"
111 #include "live_effects/lpe-angle_bisector.h"
113 #include "svg/css-ostringstream.h"
115 #include "widgets/calligraphic-profile-rename.h"
117 using Inkscape::UnitTracker;
119 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
120 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
122 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
135 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
143 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
144 static Inkscape::IconSize sizeChoices[] = {
145 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
146 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
147 Inkscape::ICON_SIZE_MENU
148 };
149 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
150 return sizeChoices[index];
151 }
153 static struct {
154 gchar const *type_name;
155 gchar const *data_name;
156 sp_verb_t verb;
157 sp_verb_t doubleclick_verb;
158 } const tools[] = {
159 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
160 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
161 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
162 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
163 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
164 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
165 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
166 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
167 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
168 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
169 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
170 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
171 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
172 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
173 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
174 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
175 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
176 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
177 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
178 { NULL, NULL, 0, 0 }
179 };
181 static struct {
182 gchar const *type_name;
183 gchar const *data_name;
184 GtkWidget *(*create_func)(SPDesktop *desktop);
185 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
186 gchar const *ui_name;
187 gint swatch_verb_id;
188 gchar const *swatch_tool;
189 gchar const *swatch_tip;
190 } const aux_toolboxes[] = {
191 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
192 SP_VERB_INVALID, 0, 0},
193 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
194 SP_VERB_INVALID, 0, 0},
195 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
196 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
197 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
198 SP_VERB_INVALID, 0, 0},
199 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
200 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
201 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
202 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
203 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
204 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
205 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
206 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
207 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
208 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
209 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
210 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
211 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
212 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
213 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
214 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
215 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
216 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
217 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
218 SP_VERB_CONTEXT_LPETOOL_PREFS, "tools.lpetool", _("TBD")},
219 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
220 SP_VERB_INVALID, 0, 0},
221 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
222 SP_VERB_INVALID, 0, 0},
223 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
224 SP_VERB_INVALID, 0, 0},
225 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
226 SP_VERB_INVALID, 0, 0},
227 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
228 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
229 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
230 };
232 #define TOOLBAR_SLIDER_HINT "full"
234 static gchar const * ui_descr =
235 "<ui>"
236 " <toolbar name='SelectToolbar'>"
237 " <toolitem action='EditSelectAll' />"
238 " <toolitem action='EditSelectAllInAllLayers' />"
239 " <toolitem action='EditDeselect' />"
240 " <separator />"
241 " <toolitem action='ObjectRotate90CCW' />"
242 " <toolitem action='ObjectRotate90' />"
243 " <toolitem action='ObjectFlipHorizontally' />"
244 " <toolitem action='ObjectFlipVertically' />"
245 " <separator />"
246 " <toolitem action='SelectionToBack' />"
247 " <toolitem action='SelectionLower' />"
248 " <toolitem action='SelectionRaise' />"
249 " <toolitem action='SelectionToFront' />"
250 " <separator />"
251 " <toolitem action='XAction' />"
252 " <toolitem action='YAction' />"
253 " <toolitem action='WidthAction' />"
254 " <toolitem action='LockAction' />"
255 " <toolitem action='HeightAction' />"
256 " <toolitem action='UnitsAction' />"
257 " <separator />"
258 " <toolitem action='transform_affect_label' />"
259 " <toolitem action='transform_stroke' />"
260 " <toolitem action='transform_corners' />"
261 " <toolitem action='transform_gradient' />"
262 " <toolitem action='transform_pattern' />"
263 " </toolbar>"
265 " <toolbar name='NodeToolbar'>"
266 " <toolitem action='NodeInsertAction' />"
267 " <toolitem action='NodeDeleteAction' />"
268 " <separator />"
269 " <toolitem action='NodeJoinAction' />"
270 " <toolitem action='NodeBreakAction' />"
271 " <separator />"
272 " <toolitem action='NodeJoinSegmentAction' />"
273 " <toolitem action='NodeDeleteSegmentAction' />"
274 " <separator />"
275 " <toolitem action='NodeCuspAction' />"
276 " <toolitem action='NodeSmoothAction' />"
277 " <toolitem action='NodeSymmetricAction' />"
278 " <separator />"
279 " <toolitem action='NodeLineAction' />"
280 " <toolitem action='NodeCurveAction' />"
281 " <separator />"
282 " <toolitem action='ObjectToPath' />"
283 " <toolitem action='StrokeToPath' />"
284 " <separator />"
285 " <toolitem action='NodeXAction' />"
286 " <toolitem action='NodeYAction' />"
287 " <toolitem action='NodeUnitsAction' />"
288 " <separator />"
289 " <toolitem action='ObjectEditClipPathAction' />"
290 " <toolitem action='ObjectEditMaskPathAction' />"
291 " <toolitem action='EditNextLPEParameterAction' />"
292 " <separator />"
293 " <toolitem action='NodesShowHandlesAction' />"
294 " <toolitem action='NodesShowHelperpath' />"
295 " </toolbar>"
297 " <toolbar name='TweakToolbar'>"
298 " <toolitem action='TweakWidthAction' />"
299 " <separator />"
300 " <toolitem action='TweakForceAction' />"
301 " <toolitem action='TweakPressureAction' />"
302 " <separator />"
303 " <toolitem action='TweakModeAction' />"
304 " <separator />"
305 " <toolitem action='TweakFidelityAction' />"
306 " <separator />"
307 " <toolitem action='TweakChannelsLabel' />"
308 " <toolitem action='TweakDoH' />"
309 " <toolitem action='TweakDoS' />"
310 " <toolitem action='TweakDoL' />"
311 " <toolitem action='TweakDoO' />"
312 " </toolbar>"
314 " <toolbar name='ZoomToolbar'>"
315 " <toolitem action='ZoomIn' />"
316 " <toolitem action='ZoomOut' />"
317 " <separator />"
318 " <toolitem action='Zoom1:0' />"
319 " <toolitem action='Zoom1:2' />"
320 " <toolitem action='Zoom2:1' />"
321 " <separator />"
322 " <toolitem action='ZoomSelection' />"
323 " <toolitem action='ZoomDrawing' />"
324 " <toolitem action='ZoomPage' />"
325 " <toolitem action='ZoomPageWidth' />"
326 " <separator />"
327 " <toolitem action='ZoomPrev' />"
328 " <toolitem action='ZoomNext' />"
329 " </toolbar>"
331 " <toolbar name='StarToolbar'>"
332 " <separator />"
333 " <toolitem action='StarStateAction' />"
334 " <separator />"
335 " <toolitem action='FlatAction' />"
336 " <separator />"
337 " <toolitem action='MagnitudeAction' />"
338 " <toolitem action='SpokeAction' />"
339 " <toolitem action='RoundednessAction' />"
340 " <toolitem action='RandomizationAction' />"
341 " <separator />"
342 " <toolitem action='StarResetAction' />"
343 " </toolbar>"
345 " <toolbar name='RectToolbar'>"
346 " <toolitem action='RectStateAction' />"
347 " <toolitem action='RectWidthAction' />"
348 " <toolitem action='RectHeightAction' />"
349 " <toolitem action='RadiusXAction' />"
350 " <toolitem action='RadiusYAction' />"
351 " <toolitem action='RectUnitsAction' />"
352 " <separator />"
353 " <toolitem action='RectResetAction' />"
354 " </toolbar>"
356 " <toolbar name='3DBoxToolbar'>"
357 " <toolitem action='3DBoxAngleXAction' />"
358 " <toolitem action='3DBoxVPXStateAction' />"
359 " <separator />"
360 " <toolitem action='3DBoxAngleYAction' />"
361 " <toolitem action='3DBoxVPYStateAction' />"
362 " <separator />"
363 " <toolitem action='3DBoxAngleZAction' />"
364 " <toolitem action='3DBoxVPZStateAction' />"
365 " </toolbar>"
367 " <toolbar name='SpiralToolbar'>"
368 " <toolitem action='SpiralStateAction' />"
369 " <toolitem action='SpiralRevolutionAction' />"
370 " <toolitem action='SpiralExpansionAction' />"
371 " <toolitem action='SpiralT0Action' />"
372 " <separator />"
373 " <toolitem action='SpiralResetAction' />"
374 " </toolbar>"
376 " <toolbar name='PenToolbar'>"
377 " <toolitem action='FreehandModeActionPen' />"
378 " <separator />"
379 " <toolitem action='SetPenShapeAction'/>"
380 " </toolbar>"
382 " <toolbar name='PencilToolbar'>"
383 " <toolitem action='FreehandModeActionPencil' />"
384 " <separator />"
385 " <toolitem action='PencilToleranceAction' />"
386 " <separator />"
387 " <toolitem action='PencilResetAction' />"
388 " <separator />"
389 " <toolitem action='SetPencilShapeAction'/>"
390 " </toolbar>"
392 " <toolbar name='CalligraphyToolbar'>"
393 " <separator />"
394 " <toolitem action='SetProfileAction'/>"
395 " <separator />"
396 " <toolitem action='CalligraphyWidthAction' />"
397 " <toolitem action='PressureAction' />"
398 " <toolitem action='TraceAction' />"
399 " <toolitem action='ThinningAction' />"
400 " <separator />"
401 " <toolitem action='AngleAction' />"
402 " <toolitem action='TiltAction' />"
403 " <toolitem action='FixationAction' />"
404 " <separator />"
405 " <toolitem action='CapRoundingAction' />"
406 " <separator />"
407 " <toolitem action='TremorAction' />"
408 " <toolitem action='WiggleAction' />"
409 " <toolitem action='MassAction' />"
410 " <separator />"
411 " </toolbar>"
413 " <toolbar name='ArcToolbar'>"
414 " <toolitem action='ArcStateAction' />"
415 " <separator />"
416 " <toolitem action='ArcStartAction' />"
417 " <toolitem action='ArcEndAction' />"
418 " <separator />"
419 " <toolitem action='ArcOpenAction' />"
420 " <separator />"
421 " <toolitem action='ArcResetAction' />"
422 " <separator />"
423 " </toolbar>"
425 " <toolbar name='PaintbucketToolbar'>"
426 " <toolitem action='ChannelsAction' />"
427 " <separator />"
428 " <toolitem action='ThresholdAction' />"
429 " <separator />"
430 " <toolitem action='OffsetAction' />"
431 " <toolitem action='PaintbucketUnitsAction' />"
432 " <separator />"
433 " <toolitem action='AutoGapAction' />"
434 " <separator />"
435 " <toolitem action='PaintbucketResetAction' />"
436 " </toolbar>"
438 " <toolbar name='EraserToolbar'>"
439 " <toolitem action='EraserWidthAction' />"
440 " <separator />"
441 " <toolitem action='EraserModeAction' />"
442 " </toolbar>"
444 " <toolbar name='LPEToolToolbar'>"
445 " <toolitem action='LPEToolModeAction' />"
446 " <separator />"
447 " <toolitem action='LPEShowBBoxAction' />"
448 " </toolbar>"
450 " <toolbar name='DropperToolbar'>"
451 " <toolitem action='DropperOpacityAction' />"
452 " <toolitem action='DropperPickAlphaAction' />"
453 " <toolitem action='DropperSetAlphaAction' />"
454 " </toolbar>"
456 " <toolbar name='ConnectorToolbar'>"
457 " <toolitem action='ConnectorAvoidAction' />"
458 " <toolitem action='ConnectorIgnoreAction' />"
459 " <toolitem action='ConnectorSpacingAction' />"
460 " <toolitem action='ConnectorGraphAction' />"
461 " <toolitem action='ConnectorLengthAction' />"
462 " <toolitem action='ConnectorDirectedAction' />"
463 " <toolitem action='ConnectorOverlapAction' />"
464 " </toolbar>"
466 "</ui>"
467 ;
469 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
471 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
473 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
474 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
476 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
477 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
479 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
480 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
484 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
485 Inkscape::UI::View::View *view, GtkTooltips *tt);
487 class VerbAction : public Gtk::Action {
488 public:
489 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
491 virtual ~VerbAction();
492 virtual void set_active(bool active = true);
494 protected:
495 virtual Gtk::Widget* create_menu_item_vfunc();
496 virtual Gtk::Widget* create_tool_item_vfunc();
498 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
499 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
501 virtual void on_activate();
503 private:
504 Inkscape::Verb* verb;
505 Inkscape::Verb* verb2;
506 Inkscape::UI::View::View *view;
507 GtkTooltips *tooltips;
508 bool active;
510 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
511 };
514 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
515 {
516 Glib::RefPtr<VerbAction> result;
517 SPAction *action = verb->get_action(view);
518 if ( action ) {
519 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
520 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
521 }
523 return result;
524 }
526 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
527 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
528 verb(verb),
529 verb2(verb2),
530 view(view),
531 tooltips(tooltips),
532 active(false)
533 {
534 }
536 VerbAction::~VerbAction()
537 {
538 }
540 Gtk::Widget* VerbAction::create_menu_item_vfunc()
541 {
542 // First call in to get the icon rendered if present in SVG
543 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
544 delete widget;
545 widget = 0;
547 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
548 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
549 return widg;
550 }
552 Gtk::Widget* VerbAction::create_tool_item_vfunc()
553 {
554 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
555 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
556 GtkWidget* toolbox = 0;
557 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
558 SP_BUTTON_TYPE_TOGGLE,
559 verb,
560 verb2,
561 view,
562 tooltips );
563 if ( active ) {
564 sp_button_toggle_set_down( SP_BUTTON(button), active);
565 }
566 gtk_widget_show_all( button );
567 Gtk::Widget* wrapped = Glib::wrap(button);
568 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
569 holder->add(*wrapped);
571 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
572 return holder;
573 }
575 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
576 {
577 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
578 Gtk::Action::connect_proxy_vfunc(proxy);
579 }
581 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
582 {
583 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
584 Gtk::Action::disconnect_proxy_vfunc(proxy);
585 }
587 void VerbAction::set_active(bool active)
588 {
589 this->active = active;
590 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
591 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
592 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
593 if (ti) {
594 // *should* have one child that is the SPButton
595 Gtk::Widget* child = ti->get_child();
596 if ( child && SP_IS_BUTTON(child->gobj()) ) {
597 SPButton* button = SP_BUTTON(child->gobj());
598 sp_button_toggle_set_down( button, active );
599 }
600 }
601 }
602 }
604 void VerbAction::on_activate()
605 {
606 if ( verb ) {
607 SPAction *action = verb->get_action(view);
608 if ( action ) {
609 sp_action_perform(action, 0);
610 }
611 }
612 }
614 /* Global text entry widgets necessary for update */
615 /* GtkWidget *dropper_rgb_entry,
616 *dropper_opacity_entry ; */
617 // should be made a private member once this is converted to class
619 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
620 connection->disconnect();
621 delete connection;
622 }
624 static void purge_repr_listener( GObject* obj, GObject* tbl )
625 {
626 (void)obj;
627 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
628 if (oldrepr) { // remove old listener
629 sp_repr_remove_listener_by_data(oldrepr, tbl);
630 Inkscape::GC::release(oldrepr);
631 oldrepr = 0;
632 g_object_set_data( tbl, "repr", NULL );
633 }
634 }
636 GtkWidget *
637 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
638 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
639 Inkscape::UI::View::View *view, GtkTooltips *tt)
640 {
641 SPAction *action = verb->get_action(view);
642 if (!action) return NULL;
644 SPAction *doubleclick_action;
645 if (doubleclick_verb)
646 doubleclick_action = doubleclick_verb->get_action(view);
647 else
648 doubleclick_action = NULL;
650 /* fixme: Handle sensitive/unsensitive */
651 /* fixme: Implement sp_button_new_from_action */
652 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
653 gtk_widget_show(b);
656 unsigned int shortcut = sp_shortcut_get_primary(verb);
657 if (shortcut) {
658 gchar key[256];
659 sp_ui_shortcut_string(shortcut, key);
660 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
661 if ( t ) {
662 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
663 }
664 g_free(tip);
665 } else {
666 if ( t ) {
667 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
668 }
669 }
671 return b;
672 }
675 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
676 {
677 SPAction* targetAction = SP_ACTION(user_data);
678 if ( targetAction ) {
679 sp_action_perform( targetAction, NULL );
680 }
681 }
683 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
684 {
685 if ( data ) {
686 GtkAction* act = GTK_ACTION(data);
687 gtk_action_set_sensitive( act, sensitive );
688 }
689 }
691 static SPActionEventVector action_event_vector = {
692 {NULL},
693 NULL,
694 NULL,
695 sp_action_action_set_sensitive,
696 NULL,
697 NULL
698 };
700 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
701 {
702 GtkAction* act = 0;
704 SPAction* targetAction = verb->get_action(view);
705 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
706 act = GTK_ACTION(inky);
707 gtk_action_set_sensitive( act, targetAction->sensitive );
709 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
711 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
712 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
714 return act;
715 }
717 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
718 {
719 Inkscape::UI::View::View *view = desktop;
720 gint verbsToUse[] = {
721 // disabled until we have icons for them:
722 //find
723 //SP_VERB_EDIT_TILE,
724 //SP_VERB_EDIT_UNTILE,
725 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
726 SP_VERB_DIALOG_DISPLAY,
727 SP_VERB_DIALOG_FILL_STROKE,
728 SP_VERB_DIALOG_NAMEDVIEW,
729 SP_VERB_DIALOG_TEXT,
730 SP_VERB_DIALOG_XML_EDITOR,
731 SP_VERB_EDIT_CLONE,
732 SP_VERB_EDIT_COPY,
733 SP_VERB_EDIT_CUT,
734 SP_VERB_EDIT_DUPLICATE,
735 SP_VERB_EDIT_PASTE,
736 SP_VERB_EDIT_REDO,
737 SP_VERB_EDIT_UNDO,
738 SP_VERB_EDIT_UNLINK_CLONE,
739 SP_VERB_FILE_EXPORT,
740 SP_VERB_FILE_IMPORT,
741 SP_VERB_FILE_NEW,
742 SP_VERB_FILE_OPEN,
743 SP_VERB_FILE_PRINT,
744 SP_VERB_FILE_SAVE,
745 SP_VERB_OBJECT_TO_CURVE,
746 SP_VERB_SELECTION_GROUP,
747 SP_VERB_SELECTION_OUTLINE,
748 SP_VERB_SELECTION_UNGROUP,
749 SP_VERB_ZOOM_1_1,
750 SP_VERB_ZOOM_1_2,
751 SP_VERB_ZOOM_2_1,
752 SP_VERB_ZOOM_DRAWING,
753 SP_VERB_ZOOM_IN,
754 SP_VERB_ZOOM_NEXT,
755 SP_VERB_ZOOM_OUT,
756 SP_VERB_ZOOM_PAGE,
757 SP_VERB_ZOOM_PAGE_WIDTH,
758 SP_VERB_ZOOM_PREV,
759 SP_VERB_ZOOM_SELECTION,
760 };
762 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
764 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
765 Glib::RefPtr<Gtk::ActionGroup> mainActions;
766 if ( groups.find(desktop) != groups.end() ) {
767 mainActions = groups[desktop];
768 }
770 if ( !mainActions ) {
771 mainActions = Gtk::ActionGroup::create("main");
772 groups[desktop] = mainActions;
773 }
775 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
776 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
777 if ( verb ) {
778 if (!mainActions->get_action(verb->get_id())) {
779 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
780 mainActions->add(Glib::wrap(act));
781 }
782 }
783 }
785 if ( !mainActions->get_action("ToolZoom") ) {
786 GtkTooltips *tt = gtk_tooltips_new();
787 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
788 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
789 if ( va ) {
790 mainActions->add(va);
791 if ( i == 0 ) {
792 va->set_active(true);
793 }
794 }
795 }
796 }
799 return mainActions;
800 }
803 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
804 {
805 gtk_widget_set_size_request( widget,
806 widget->allocation.width,
807 widget->allocation.height );
808 }
810 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
811 {
812 gtk_widget_set_size_request( widget, -1, -1 );
813 }
817 GtkWidget *
818 sp_tool_toolbox_new()
819 {
820 GtkTooltips *tt = gtk_tooltips_new();
821 GtkWidget* tb = gtk_toolbar_new();
822 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
823 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
825 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
826 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
828 gtk_widget_set_sensitive(tb, FALSE);
830 GtkWidget *hb = gtk_handle_box_new();
831 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
832 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
833 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
835 gtk_container_add(GTK_CONTAINER(hb), tb);
836 gtk_widget_show(GTK_WIDGET(tb));
838 sigc::connection* conn = new sigc::connection;
839 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
841 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
842 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
844 return hb;
845 }
847 GtkWidget *
848 sp_aux_toolbox_new()
849 {
850 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
852 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
854 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
856 gtk_widget_set_sensitive(tb, FALSE);
858 GtkWidget *hb = gtk_handle_box_new();
859 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
860 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
861 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
863 gtk_container_add(GTK_CONTAINER(hb), tb);
864 gtk_widget_show(GTK_WIDGET(tb));
866 sigc::connection* conn = new sigc::connection;
867 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
869 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
870 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
872 return hb;
873 }
875 //####################################
876 //# Commands Bar
877 //####################################
879 GtkWidget *
880 sp_commands_toolbox_new()
881 {
882 GtkWidget *tb = gtk_toolbar_new();
884 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
885 gtk_widget_set_sensitive(tb, FALSE);
887 GtkWidget *hb = gtk_handle_box_new();
888 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
889 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
890 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
892 gtk_container_add(GTK_CONTAINER(hb), tb);
893 gtk_widget_show(GTK_WIDGET(tb));
895 sigc::connection* conn = new sigc::connection;
896 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
898 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
899 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
901 return hb;
902 }
905 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
906 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
907 gchar const *path, gchar const *data, gdouble def,
908 GtkWidget *focusTarget,
909 GtkWidget *us,
910 GObject *dataKludge,
911 gboolean altx, gchar const *altx_mark,
912 gdouble lower, gdouble upper, gdouble step, gdouble page,
913 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
914 void (*callback)(GtkAdjustment *, GObject *),
915 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
916 {
917 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
918 lower, upper, step, page, page ) );
919 if (us) {
920 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
921 }
923 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
925 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
926 if ( shortLabel ) {
927 g_object_set( act, "short_label", shortLabel, NULL );
928 }
930 if ( (descrCount > 0) && descrLabels && descrValues ) {
931 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
932 }
934 if ( focusTarget ) {
935 ege_adjustment_action_set_focuswidget( act, focusTarget );
936 }
938 if ( altx && altx_mark ) {
939 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
940 }
942 if ( dataKludge ) {
943 g_object_set_data( dataKludge, data, adj );
944 }
946 // Using a cast just to make sure we pass in the right kind of function pointer
947 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
949 return act;
950 }
953 //####################################
954 //# node editing callbacks
955 //####################################
957 /**
958 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
959 */
960 static ShapeEditor *get_current_shape_editor()
961 {
962 if (!SP_ACTIVE_DESKTOP) {
963 return NULL;
964 }
966 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
968 if (!SP_IS_NODE_CONTEXT(event_context)) {
969 return NULL;
970 }
972 return SP_NODE_CONTEXT(event_context)->shape_editor;
973 }
976 void
977 sp_node_path_edit_add(void)
978 {
979 ShapeEditor *shape_editor = get_current_shape_editor();
980 if (shape_editor) shape_editor->add_node();
981 }
983 void
984 sp_node_path_edit_delete(void)
985 {
986 ShapeEditor *shape_editor = get_current_shape_editor();
987 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
988 }
990 void
991 sp_node_path_edit_delete_segment(void)
992 {
993 ShapeEditor *shape_editor = get_current_shape_editor();
994 if (shape_editor) shape_editor->delete_segment();
995 }
997 void
998 sp_node_path_edit_break(void)
999 {
1000 ShapeEditor *shape_editor = get_current_shape_editor();
1001 if (shape_editor) shape_editor->break_at_nodes();
1002 }
1004 void
1005 sp_node_path_edit_join(void)
1006 {
1007 ShapeEditor *shape_editor = get_current_shape_editor();
1008 if (shape_editor) shape_editor->join_nodes();
1009 }
1011 void
1012 sp_node_path_edit_join_segment(void)
1013 {
1014 ShapeEditor *shape_editor = get_current_shape_editor();
1015 if (shape_editor) shape_editor->join_segments();
1016 }
1018 void
1019 sp_node_path_edit_toline(void)
1020 {
1021 ShapeEditor *shape_editor = get_current_shape_editor();
1022 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1023 }
1025 void
1026 sp_node_path_edit_tocurve(void)
1027 {
1028 ShapeEditor *shape_editor = get_current_shape_editor();
1029 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1030 }
1032 void
1033 sp_node_path_edit_cusp(void)
1034 {
1035 ShapeEditor *shape_editor = get_current_shape_editor();
1036 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1037 }
1039 void
1040 sp_node_path_edit_smooth(void)
1041 {
1042 ShapeEditor *shape_editor = get_current_shape_editor();
1043 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1044 }
1046 void
1047 sp_node_path_edit_symmetrical(void)
1048 {
1049 ShapeEditor *shape_editor = get_current_shape_editor();
1050 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1051 }
1053 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1054 bool show = gtk_toggle_action_get_active( act );
1055 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1056 ShapeEditor *shape_editor = get_current_shape_editor();
1057 if (shape_editor) shape_editor->show_handles(show);
1058 }
1060 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1061 bool show = gtk_toggle_action_get_active( act );
1062 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1063 ShapeEditor *shape_editor = get_current_shape_editor();
1064 if (shape_editor) shape_editor->show_helperpath(show);
1065 }
1067 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1068 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1069 }
1071 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1072 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1073 }
1075 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1076 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1077 }
1079 /* is called when the node selection is modified */
1080 static void
1081 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1082 {
1083 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1084 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1085 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1086 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1088 // quit if run by the attr_changed listener
1089 if (g_object_get_data( tbl, "freeze" )) {
1090 return;
1091 }
1093 // in turn, prevent listener from responding
1094 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1096 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1097 SPUnit const *unit = tracker->getActiveUnit();
1099 ShapeEditor *shape_editor = get_current_shape_editor();
1100 if (shape_editor && shape_editor->has_nodepath()) {
1101 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1102 int n_selected = 0;
1103 if (nodepath) {
1104 n_selected = nodepath->numSelected();
1105 }
1107 if (n_selected == 0) {
1108 gtk_action_set_sensitive(xact, FALSE);
1109 gtk_action_set_sensitive(yact, FALSE);
1110 } else {
1111 gtk_action_set_sensitive(xact, TRUE);
1112 gtk_action_set_sensitive(yact, TRUE);
1113 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1114 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1116 if (n_selected == 1) {
1117 Geom::Point sel_node = nodepath->singleSelectedCoords();
1118 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1119 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1120 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1121 }
1122 } else {
1123 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1124 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1125 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1126 /* Note: Currently x and y will always have a value, even if the coordinates of the
1127 selected nodes don't coincide (in this case we use the coordinates of the center
1128 of the bounding box). So the entries are never set to zero. */
1129 // FIXME: Maybe we should clear the entry if several nodes are selected
1130 // instead of providing a kind of average value
1131 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1132 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1133 }
1134 }
1135 }
1136 } else {
1137 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1138 gtk_action_set_sensitive(xact, FALSE);
1139 gtk_action_set_sensitive(yact, FALSE);
1140 }
1142 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1143 }
1145 static void
1146 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1147 {
1148 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1150 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1151 SPUnit const *unit = tracker->getActiveUnit();
1153 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1154 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1155 }
1157 // quit if run by the attr_changed listener
1158 if (g_object_get_data( tbl, "freeze" )) {
1159 return;
1160 }
1162 // in turn, prevent listener from responding
1163 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1165 ShapeEditor *shape_editor = get_current_shape_editor();
1166 if (shape_editor && shape_editor->has_nodepath()) {
1167 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1168 if (!strcmp(value_name, "x")) {
1169 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1170 }
1171 if (!strcmp(value_name, "y")) {
1172 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1173 }
1174 }
1176 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1177 }
1179 static void
1180 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1181 {
1182 sp_node_path_value_changed(adj, tbl, "x");
1183 }
1185 static void
1186 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1187 {
1188 sp_node_path_value_changed(adj, tbl, "y");
1189 }
1191 void
1192 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1193 {
1194 {
1195 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1196 SPItem *item = selection->singleItem();
1197 if (item && SP_IS_LPE_ITEM(item)) {
1198 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1199 gtk_action_set_sensitive(w, TRUE);
1200 } else {
1201 gtk_action_set_sensitive(w, FALSE);
1202 }
1203 } else {
1204 gtk_action_set_sensitive(w, FALSE);
1205 }
1206 }
1208 {
1209 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1210 SPItem *item = selection->singleItem();
1211 if (item && item->clip_ref && item->clip_ref->getObject()) {
1212 gtk_action_set_sensitive(w, TRUE);
1213 } else {
1214 gtk_action_set_sensitive(w, FALSE);
1215 }
1216 }
1218 {
1219 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1220 SPItem *item = selection->singleItem();
1221 if (item && item->mask_ref && item->mask_ref->getObject()) {
1222 gtk_action_set_sensitive(w, TRUE);
1223 } else {
1224 gtk_action_set_sensitive(w, FALSE);
1225 }
1226 }
1227 }
1229 void
1230 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1231 {
1232 sp_node_toolbox_sel_changed (selection, tbl);
1233 }
1237 //################################
1238 //## Node Editing Toolbox ##
1239 //################################
1241 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1242 {
1243 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1244 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1245 g_object_set_data( holder, "tracker", tracker );
1247 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1249 {
1250 InkAction* inky = ink_action_new( "NodeInsertAction",
1251 _("Insert node"),
1252 _("Insert new nodes into selected segments"),
1253 "node_insert",
1254 secondarySize );
1255 g_object_set( inky, "short_label", _("Insert"), NULL );
1256 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1257 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1258 }
1260 {
1261 InkAction* inky = ink_action_new( "NodeDeleteAction",
1262 _("Delete node"),
1263 _("Delete selected nodes"),
1264 "node_delete",
1265 secondarySize );
1266 g_object_set( inky, "short_label", _("Delete"), NULL );
1267 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1268 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1269 }
1271 {
1272 InkAction* inky = ink_action_new( "NodeJoinAction",
1273 _("Join endnodes"),
1274 _("Join selected endnodes"),
1275 "node_join",
1276 secondarySize );
1277 g_object_set( inky, "short_label", _("Join"), NULL );
1278 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1279 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1280 }
1282 {
1283 InkAction* inky = ink_action_new( "NodeBreakAction",
1284 _("Break nodes"),
1285 _("Break path at selected nodes"),
1286 "node_break",
1287 secondarySize );
1288 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1289 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1290 }
1293 {
1294 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1295 _("Join with segment"),
1296 _("Join selected endnodes with a new segment"),
1297 "node_join_segment",
1298 secondarySize );
1299 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1300 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1301 }
1303 {
1304 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1305 _("Delete segment"),
1306 _("Delete segment between two non-endpoint nodes"),
1307 "node_delete_segment",
1308 secondarySize );
1309 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1310 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1311 }
1313 {
1314 InkAction* inky = ink_action_new( "NodeCuspAction",
1315 _("Node Cusp"),
1316 _("Make selected nodes corner"),
1317 "node_cusp",
1318 secondarySize );
1319 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1320 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1321 }
1323 {
1324 InkAction* inky = ink_action_new( "NodeSmoothAction",
1325 _("Node Smooth"),
1326 _("Make selected nodes smooth"),
1327 "node_smooth",
1328 secondarySize );
1329 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1330 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1331 }
1333 {
1334 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1335 _("Node Symmetric"),
1336 _("Make selected nodes symmetric"),
1337 "node_symmetric",
1338 secondarySize );
1339 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1340 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1341 }
1343 {
1344 InkAction* inky = ink_action_new( "NodeLineAction",
1345 _("Node Line"),
1346 _("Make selected segments lines"),
1347 "node_line",
1348 secondarySize );
1349 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1350 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1351 }
1353 {
1354 InkAction* inky = ink_action_new( "NodeCurveAction",
1355 _("Node Curve"),
1356 _("Make selected segments curves"),
1357 "node_curve",
1358 secondarySize );
1359 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1360 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1361 }
1363 {
1364 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1365 _("Show Handles"),
1366 _("Show the Bezier handles of selected nodes"),
1367 "nodes_show_handles",
1368 Inkscape::ICON_SIZE_DECORATION );
1369 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1370 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1371 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1372 }
1374 {
1375 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1376 _("Show Outline"),
1377 _("Show the outline of the path"),
1378 "nodes_show_helperpath",
1379 Inkscape::ICON_SIZE_DECORATION );
1380 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1381 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1382 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1383 }
1385 {
1386 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1387 _("Next path effect parameter"),
1388 _("Show next path effect parameter for editing"),
1389 "edit_next_parameter",
1390 Inkscape::ICON_SIZE_DECORATION );
1391 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1392 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1393 g_object_set_data( holder, "nodes_lpeedit", inky);
1394 }
1396 {
1397 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1398 _("Edit clipping path"),
1399 _("Edit the clipping path of the object"),
1400 "nodeedit-clippath",
1401 Inkscape::ICON_SIZE_DECORATION );
1402 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1403 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404 g_object_set_data( holder, "nodes_clippathedit", inky);
1405 }
1407 {
1408 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1409 _("Edit mask path"),
1410 _("Edit the mask of the object"),
1411 "nodeedit-mask",
1412 Inkscape::ICON_SIZE_DECORATION );
1413 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1414 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1415 g_object_set_data( holder, "nodes_maskedit", inky);
1416 }
1418 /* X coord of selected node(s) */
1419 {
1420 EgeAdjustmentAction* eact = 0;
1421 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1422 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1423 eact = create_adjustment_action( "NodeXAction",
1424 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1425 "tools.nodes", "Xcoord", 0,
1426 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1427 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1428 labels, values, G_N_ELEMENTS(labels),
1429 sp_node_path_x_value_changed );
1430 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1431 g_object_set_data( holder, "nodes_x_action", eact );
1432 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1433 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1434 }
1436 /* Y coord of selected node(s) */
1437 {
1438 EgeAdjustmentAction* eact = 0;
1439 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1440 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1441 eact = create_adjustment_action( "NodeYAction",
1442 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1443 "tools.nodes", "Ycoord", 0,
1444 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1445 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1446 labels, values, G_N_ELEMENTS(labels),
1447 sp_node_path_y_value_changed );
1448 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1449 g_object_set_data( holder, "nodes_y_action", eact );
1450 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1451 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1452 }
1454 // add the units menu
1455 {
1456 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1457 gtk_action_group_add_action( mainActions, act );
1458 }
1461 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1463 //watch selection
1464 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1466 sigc::connection *c_selection_changed =
1467 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1468 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1469 pool->add_connection ("selection-changed", c_selection_changed);
1471 sigc::connection *c_selection_modified =
1472 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1473 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1474 pool->add_connection ("selection-modified", c_selection_modified);
1476 sigc::connection *c_subselection_changed =
1477 new sigc::connection (desktop->connectToolSubselectionChanged
1478 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1479 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1481 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1483 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1484 } // end of sp_node_toolbox_prep()
1487 //########################
1488 //## Zoom Toolbox ##
1489 //########################
1491 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1492 {
1493 // no custom GtkAction setup needed
1494 } // end of sp_zoom_toolbox_prep()
1496 void
1497 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1498 {
1499 toolbox_set_desktop(toolbox,
1500 desktop,
1501 setup_tool_toolbox,
1502 update_tool_toolbox,
1503 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1504 "event_context_connection")));
1505 }
1508 void
1509 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1510 {
1511 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1512 desktop,
1513 setup_aux_toolbox,
1514 update_aux_toolbox,
1515 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1516 "event_context_connection")));
1517 }
1519 void
1520 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1521 {
1522 toolbox_set_desktop(toolbox,
1523 desktop,
1524 setup_commands_toolbox,
1525 update_commands_toolbox,
1526 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1527 "event_context_connection")));
1528 }
1530 static void
1531 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1532 {
1533 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1534 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1536 if (old_desktop) {
1537 GList *children, *iter;
1539 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1540 for ( iter = children ; iter ; iter = iter->next ) {
1541 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1542 }
1543 g_list_free(children);
1544 }
1546 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1548 if (desktop) {
1549 gtk_widget_set_sensitive(toolbox, TRUE);
1550 setup_func(toolbox, desktop);
1551 update_func(desktop, desktop->event_context, toolbox);
1552 *conn = desktop->connectEventContextChanged
1553 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1554 } else {
1555 gtk_widget_set_sensitive(toolbox, FALSE);
1556 }
1558 } // end of toolbox_set_desktop()
1561 static void
1562 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1563 {
1564 gchar const * descr =
1565 "<ui>"
1566 " <toolbar name='ToolToolbar'>"
1567 " <toolitem action='ToolSelector' />"
1568 " <toolitem action='ToolNode' />"
1569 " <toolitem action='ToolTweak' />"
1570 " <toolitem action='ToolZoom' />"
1571 " <toolitem action='ToolRect' />"
1572 " <toolitem action='Tool3DBox' />"
1573 " <toolitem action='ToolArc' />"
1574 " <toolitem action='ToolStar' />"
1575 " <toolitem action='ToolSpiral' />"
1576 " <toolitem action='ToolPencil' />"
1577 " <toolitem action='ToolPen' />"
1578 " <toolitem action='ToolCalligraphic' />"
1579 " <toolitem action='ToolEraser' />"
1580 " <toolitem action='ToolLPETool' />"
1581 " <toolitem action='ToolPaintBucket' />"
1582 " <toolitem action='ToolText' />"
1583 " <toolitem action='ToolConnector' />"
1584 " <toolitem action='ToolGradient' />"
1585 " <toolitem action='ToolDropper' />"
1586 " </toolbar>"
1587 "</ui>";
1588 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1589 GtkUIManager* mgr = gtk_ui_manager_new();
1590 GError* errVal = 0;
1592 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1593 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1595 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1596 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1597 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1598 }
1599 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1600 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1602 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1603 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1605 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1607 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1608 if ( child ) {
1609 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1610 }
1612 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1613 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1614 }
1617 static void
1618 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1619 {
1620 gchar const *const tname = ( eventcontext
1621 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1622 : NULL );
1623 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1625 for (int i = 0 ; tools[i].type_name ; i++ ) {
1626 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1627 if ( act ) {
1628 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1629 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1630 if ( verbAct ) {
1631 verbAct->set_active(setActive);
1632 }
1633 }
1634 }
1635 }
1637 static void
1638 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1639 {
1640 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1641 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1642 GtkUIManager* mgr = gtk_ui_manager_new();
1643 GError* errVal = 0;
1644 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1645 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1647 std::map<std::string, GtkWidget*> dataHolders;
1649 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1650 if ( aux_toolboxes[i].prep_func ) {
1651 // converted to GtkActions and UIManager
1653 GtkWidget* kludge = gtk_toolbar_new();
1654 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1655 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1656 dataHolders[aux_toolboxes[i].type_name] = kludge;
1657 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1658 } else {
1660 GtkWidget *sub_toolbox = 0;
1661 if (aux_toolboxes[i].create_func == NULL)
1662 sub_toolbox = sp_empty_toolbox_new(desktop);
1663 else {
1664 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1665 }
1667 gtk_size_group_add_widget( grouper, sub_toolbox );
1669 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1670 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1672 }
1673 }
1675 // Second pass to create toolbars *after* all GtkActions are created
1676 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1677 if ( aux_toolboxes[i].prep_func ) {
1678 // converted to GtkActions and UIManager
1680 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1682 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1683 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1685 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1686 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1687 g_free( tmp );
1688 tmp = 0;
1690 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1691 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1692 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1693 }
1694 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1697 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1699 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1700 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1701 swatch->setDesktop( desktop );
1702 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1703 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1704 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1705 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 );
1706 }
1708 gtk_widget_show_all( holder );
1709 sp_set_font_size_smaller( holder );
1711 gtk_size_group_add_widget( grouper, holder );
1713 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1714 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1715 }
1716 }
1718 g_object_unref( G_OBJECT(grouper) );
1719 }
1721 static void
1722 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1723 {
1724 gchar const *tname = ( eventcontext
1725 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1726 : NULL );
1727 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1728 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1729 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1730 gtk_widget_show_all(sub_toolbox);
1731 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1732 } else {
1733 gtk_widget_hide(sub_toolbox);
1734 }
1735 }
1736 }
1738 static void
1739 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1740 {
1741 gchar const * descr =
1742 "<ui>"
1743 " <toolbar name='CommandsToolbar'>"
1744 " <toolitem action='FileNew' />"
1745 " <toolitem action='FileOpen' />"
1746 " <toolitem action='FileSave' />"
1747 " <toolitem action='FilePrint' />"
1748 " <separator />"
1749 " <toolitem action='FileImport' />"
1750 " <toolitem action='FileExport' />"
1751 " <separator />"
1752 " <toolitem action='EditUndo' />"
1753 " <toolitem action='EditRedo' />"
1754 " <separator />"
1755 " <toolitem action='EditCopy' />"
1756 " <toolitem action='EditCut' />"
1757 " <toolitem action='EditPaste' />"
1758 " <separator />"
1759 " <toolitem action='ZoomSelection' />"
1760 " <toolitem action='ZoomDrawing' />"
1761 " <toolitem action='ZoomPage' />"
1762 " <separator />"
1763 " <toolitem action='EditDuplicate' />"
1764 " <toolitem action='EditClone' />"
1765 " <toolitem action='EditUnlinkClone' />"
1766 " <separator />"
1767 " <toolitem action='SelectionGroup' />"
1768 " <toolitem action='SelectionUnGroup' />"
1769 " <separator />"
1770 " <toolitem action='DialogFillStroke' />"
1771 " <toolitem action='DialogText' />"
1772 " <toolitem action='DialogXMLEditor' />"
1773 " <toolitem action='DialogAlignDistribute' />"
1774 " <separator />"
1775 " <toolitem action='DialogPreferences' />"
1776 " <toolitem action='DialogDocumentProperties' />"
1777 " </toolbar>"
1778 "</ui>";
1779 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1782 GtkUIManager* mgr = gtk_ui_manager_new();
1783 GError* errVal = 0;
1785 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1786 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1788 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1789 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1790 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1791 }
1793 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1794 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1796 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1797 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1800 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1802 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1803 if ( child ) {
1804 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1805 }
1807 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1808 }
1810 static void
1811 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1812 {
1813 }
1815 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1816 {
1817 gtk_widget_show(toolbox_toplevel);
1818 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1820 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1821 if (!shown_toolbox) {
1822 return;
1823 }
1824 gtk_widget_show(toolbox);
1826 gtk_widget_show_all(shown_toolbox);
1827 }
1829 static GtkWidget *
1830 sp_empty_toolbox_new(SPDesktop *desktop)
1831 {
1832 GtkWidget *tbl = gtk_toolbar_new();
1833 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1834 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1836 gtk_widget_show_all(tbl);
1837 sp_set_font_size_smaller (tbl);
1839 return tbl;
1840 }
1842 #define MODE_LABEL_WIDTH 70
1844 //########################
1845 //## Star ##
1846 //########################
1848 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1849 {
1850 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1852 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1853 // do not remember prefs if this call is initiated by an undo change, because undoing object
1854 // creation sets bogus values to its attributes before it is deleted
1855 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1856 }
1858 // quit if run by the attr_changed listener
1859 if (g_object_get_data( dataKludge, "freeze" )) {
1860 return;
1861 }
1863 // in turn, prevent listener from responding
1864 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1866 bool modmade = false;
1868 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1869 GSList const *items = selection->itemList();
1870 for (; items != NULL; items = items->next) {
1871 if (SP_IS_STAR((SPItem *) items->data)) {
1872 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1873 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1874 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1875 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1876 + M_PI / (gint)adj->value));
1877 SP_OBJECT((SPItem *) items->data)->updateRepr();
1878 modmade = true;
1879 }
1880 }
1881 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1882 _("Star: Change number of corners"));
1884 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1885 }
1887 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1888 {
1889 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1891 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1892 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1893 }
1895 // quit if run by the attr_changed listener
1896 if (g_object_get_data( dataKludge, "freeze" )) {
1897 return;
1898 }
1900 // in turn, prevent listener from responding
1901 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1903 bool modmade = false;
1904 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1905 GSList const *items = selection->itemList();
1906 for (; items != NULL; items = items->next) {
1907 if (SP_IS_STAR((SPItem *) items->data)) {
1908 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1910 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1911 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1912 if (r2 < r1) {
1913 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1914 } else {
1915 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1916 }
1918 SP_OBJECT((SPItem *) items->data)->updateRepr();
1919 modmade = true;
1920 }
1921 }
1923 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1924 _("Star: Change spoke ratio"));
1926 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1927 }
1929 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1930 {
1931 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1932 bool flat = ege_select_one_action_get_active( act ) == 0;
1934 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1935 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1936 flat ? "true" : "false" );
1937 }
1939 // quit if run by the attr_changed listener
1940 if (g_object_get_data( dataKludge, "freeze" )) {
1941 return;
1942 }
1944 // in turn, prevent listener from responding
1945 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1947 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1948 GSList const *items = selection->itemList();
1949 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1950 bool modmade = false;
1952 if ( prop_action ) {
1953 gtk_action_set_sensitive( prop_action, !flat );
1954 }
1956 for (; items != NULL; items = items->next) {
1957 if (SP_IS_STAR((SPItem *) items->data)) {
1958 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1959 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1960 SP_OBJECT((SPItem *) items->data)->updateRepr();
1961 modmade = true;
1962 }
1963 }
1965 if (modmade) {
1966 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1967 flat ? _("Make polygon") : _("Make star"));
1968 }
1970 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1971 }
1973 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1974 {
1975 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1977 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1978 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1979 }
1981 // quit if run by the attr_changed listener
1982 if (g_object_get_data( dataKludge, "freeze" )) {
1983 return;
1984 }
1986 // in turn, prevent listener from responding
1987 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1989 bool modmade = false;
1991 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1992 GSList const *items = selection->itemList();
1993 for (; items != NULL; items = items->next) {
1994 if (SP_IS_STAR((SPItem *) items->data)) {
1995 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1996 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1997 SP_OBJECT(items->data)->updateRepr();
1998 modmade = true;
1999 }
2000 }
2001 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2002 _("Star: Change rounding"));
2004 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2005 }
2007 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2008 {
2009 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2011 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2012 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2013 }
2015 // quit if run by the attr_changed listener
2016 if (g_object_get_data( dataKludge, "freeze" )) {
2017 return;
2018 }
2020 // in turn, prevent listener from responding
2021 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2023 bool modmade = false;
2025 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2026 GSList const *items = selection->itemList();
2027 for (; items != NULL; items = items->next) {
2028 if (SP_IS_STAR((SPItem *) items->data)) {
2029 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2030 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2031 SP_OBJECT(items->data)->updateRepr();
2032 modmade = true;
2033 }
2034 }
2035 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2036 _("Star: Change randomization"));
2038 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2039 }
2042 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2043 gchar const */*old_value*/, gchar const */*new_value*/,
2044 bool /*is_interactive*/, gpointer data)
2045 {
2046 GtkWidget *tbl = GTK_WIDGET(data);
2048 // quit if run by the _changed callbacks
2049 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2050 return;
2051 }
2053 // in turn, prevent callbacks from responding
2054 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2056 GtkAdjustment *adj = 0;
2058 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2059 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2061 if (!strcmp(name, "inkscape:randomized")) {
2062 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2063 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2064 } else if (!strcmp(name, "inkscape:rounded")) {
2065 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2066 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2067 } else if (!strcmp(name, "inkscape:flatsided")) {
2068 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2069 char const *flatsides = repr->attribute("inkscape:flatsided");
2070 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2071 if ( flatsides && !strcmp(flatsides,"false") ) {
2072 ege_select_one_action_set_active( flat_action, 1 );
2073 gtk_action_set_sensitive( prop_action, TRUE );
2074 } else {
2075 ege_select_one_action_set_active( flat_action, 0 );
2076 gtk_action_set_sensitive( prop_action, FALSE );
2077 }
2078 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2079 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2080 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2081 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2082 if (r2 < r1) {
2083 gtk_adjustment_set_value(adj, r2/r1);
2084 } else {
2085 gtk_adjustment_set_value(adj, r1/r2);
2086 }
2087 } else if (!strcmp(name, "sodipodi:sides")) {
2088 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2089 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2090 }
2092 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2093 }
2096 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2097 {
2098 NULL, /* child_added */
2099 NULL, /* child_removed */
2100 star_tb_event_attr_changed,
2101 NULL, /* content_changed */
2102 NULL /* order_changed */
2103 };
2106 /**
2107 * \param selection Should not be NULL.
2108 */
2109 static void
2110 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2111 {
2112 int n_selected = 0;
2113 Inkscape::XML::Node *repr = NULL;
2115 purge_repr_listener( tbl, tbl );
2117 for (GSList const *items = selection->itemList();
2118 items != NULL;
2119 items = items->next)
2120 {
2121 if (SP_IS_STAR((SPItem *) items->data)) {
2122 n_selected++;
2123 repr = SP_OBJECT_REPR((SPItem *) items->data);
2124 }
2125 }
2127 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2129 if (n_selected == 0) {
2130 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2131 } else if (n_selected == 1) {
2132 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2134 if (repr) {
2135 g_object_set_data( tbl, "repr", repr );
2136 Inkscape::GC::anchor(repr);
2137 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2138 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2139 }
2140 } else {
2141 // FIXME: implement averaging of all parameters for multiple selected stars
2142 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2143 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2144 }
2145 }
2148 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2149 {
2150 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2151 // callbacks to lump all the changes for all selected objects in one undo step
2153 GtkAdjustment *adj = 0;
2155 // fixme: make settable in prefs!
2156 gint mag = 5;
2157 gdouble prop = 0.5;
2158 gboolean flat = FALSE;
2159 gdouble randomized = 0;
2160 gdouble rounded = 0;
2162 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2163 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2165 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2166 gtk_action_set_sensitive( sb2, !flat );
2168 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2169 gtk_adjustment_set_value(adj, mag);
2170 gtk_adjustment_value_changed(adj);
2172 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2173 gtk_adjustment_set_value(adj, prop);
2174 gtk_adjustment_value_changed(adj);
2176 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2177 gtk_adjustment_set_value(adj, rounded);
2178 gtk_adjustment_value_changed(adj);
2180 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2181 gtk_adjustment_set_value(adj, randomized);
2182 gtk_adjustment_value_changed(adj);
2183 }
2186 void
2187 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2188 {
2189 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2190 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2191 GtkWidget *l = gtk_label_new(NULL);
2192 gtk_label_set_markup(GTK_LABEL(l), title);
2193 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2194 if ( GTK_IS_TOOLBAR(tbl) ) {
2195 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2196 } else {
2197 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2198 }
2199 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2200 }
2203 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2204 {
2205 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2207 {
2208 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2209 ege_output_action_set_use_markup( act, TRUE );
2210 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2211 g_object_set_data( holder, "mode_action", act );
2212 }
2214 {
2215 EgeAdjustmentAction* eact = 0;
2216 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2217 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2219 /* Flatsided checkbox */
2220 {
2221 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2223 GtkTreeIter iter;
2224 gtk_list_store_append( model, &iter );
2225 gtk_list_store_set( model, &iter,
2226 0, _("Polygon"),
2227 1, _("Regular polygon (with one handle) instead of a star"),
2228 2, "star_flat",
2229 -1 );
2231 gtk_list_store_append( model, &iter );
2232 gtk_list_store_set( model, &iter,
2233 0, _("Star"),
2234 1, _("Star instead of a regular polygon (with one handle)"),
2235 2, "star_angled",
2236 -1 );
2238 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2239 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2240 g_object_set_data( holder, "flat_action", act );
2242 ege_select_one_action_set_appearance( act, "full" );
2243 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2244 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2245 ege_select_one_action_set_icon_column( act, 2 );
2246 ege_select_one_action_set_icon_size( act, secondarySize );
2247 ege_select_one_action_set_tooltip_column( act, 1 );
2249 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2250 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2251 }
2253 /* Magnitude */
2254 {
2255 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2256 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2257 eact = create_adjustment_action( "MagnitudeAction",
2258 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2259 "tools.shapes.star", "magnitude", 3,
2260 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2261 3, 1024, 1, 5,
2262 labels, values, G_N_ELEMENTS(labels),
2263 sp_stb_magnitude_value_changed,
2264 1.0, 0 );
2265 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2266 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2267 }
2269 /* Spoke ratio */
2270 {
2271 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2272 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2273 eact = create_adjustment_action( "SpokeAction",
2274 _("Spoke ratio"), _("Spoke ratio:"),
2275 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2276 // Base radius is the same for the closest handle.
2277 _("Base radius to tip radius ratio"),
2278 "tools.shapes.star", "proportion", 0.5,
2279 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2280 0.01, 1.0, 0.01, 0.1,
2281 labels, values, G_N_ELEMENTS(labels),
2282 sp_stb_proportion_value_changed );
2283 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2284 g_object_set_data( holder, "prop_action", eact );
2285 }
2287 if ( !isFlatSided ) {
2288 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2289 } else {
2290 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2291 }
2293 /* Roundedness */
2294 {
2295 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2296 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2297 eact = create_adjustment_action( "RoundednessAction",
2298 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2299 "tools.shapes.star", "rounded", 0.0,
2300 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2301 -10.0, 10.0, 0.01, 0.1,
2302 labels, values, G_N_ELEMENTS(labels),
2303 sp_stb_rounded_value_changed );
2304 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2305 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2306 }
2308 /* Randomization */
2309 {
2310 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2311 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2312 eact = create_adjustment_action( "RandomizationAction",
2313 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2314 "tools.shapes.star", "randomized", 0.0,
2315 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2316 -10.0, 10.0, 0.001, 0.01,
2317 labels, values, G_N_ELEMENTS(labels),
2318 sp_stb_randomized_value_changed, 0.1, 3 );
2319 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2320 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2321 }
2322 }
2324 {
2325 /* Reset */
2326 {
2327 GtkAction* act = gtk_action_new( "StarResetAction",
2328 _("Defaults"),
2329 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2330 GTK_STOCK_CLEAR );
2331 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2332 gtk_action_group_add_action( mainActions, act );
2333 gtk_action_set_sensitive( act, TRUE );
2334 }
2335 }
2337 sigc::connection *connection = new sigc::connection(
2338 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2339 );
2340 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2341 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2342 }
2345 //########################
2346 //## Rect ##
2347 //########################
2349 static void sp_rtb_sensitivize( GObject *tbl )
2350 {
2351 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2352 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2353 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2355 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2356 gtk_action_set_sensitive( not_rounded, FALSE );
2357 } else {
2358 gtk_action_set_sensitive( not_rounded, TRUE );
2359 }
2360 }
2363 static void
2364 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2365 void (*setter)(SPRect *, gdouble))
2366 {
2367 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2369 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2370 SPUnit const *unit = tracker->getActiveUnit();
2372 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2373 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2374 }
2376 // quit if run by the attr_changed listener
2377 if (g_object_get_data( tbl, "freeze" )) {
2378 return;
2379 }
2381 // in turn, prevent listener from responding
2382 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2384 bool modmade = false;
2385 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2386 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2387 if (SP_IS_RECT(items->data)) {
2388 if (adj->value != 0) {
2389 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2390 } else {
2391 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2392 }
2393 modmade = true;
2394 }
2395 }
2397 sp_rtb_sensitivize( tbl );
2399 if (modmade) {
2400 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2401 _("Change rectangle"));
2402 }
2404 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2405 }
2407 static void
2408 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2409 {
2410 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2411 }
2413 static void
2414 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2415 {
2416 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2417 }
2419 static void
2420 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2421 {
2422 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2423 }
2425 static void
2426 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2427 {
2428 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2429 }
2433 static void
2434 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2435 {
2436 GtkAdjustment *adj = 0;
2438 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2439 gtk_adjustment_set_value(adj, 0.0);
2440 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2441 gtk_adjustment_value_changed(adj);
2443 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2444 gtk_adjustment_set_value(adj, 0.0);
2445 gtk_adjustment_value_changed(adj);
2447 sp_rtb_sensitivize( obj );
2448 }
2450 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2451 gchar const */*old_value*/, gchar const */*new_value*/,
2452 bool /*is_interactive*/, gpointer data)
2453 {
2454 GObject *tbl = G_OBJECT(data);
2456 // quit if run by the _changed callbacks
2457 if (g_object_get_data( tbl, "freeze" )) {
2458 return;
2459 }
2461 // in turn, prevent callbacks from responding
2462 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2464 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2465 SPUnit const *unit = tracker->getActiveUnit();
2467 gpointer item = g_object_get_data( tbl, "item" );
2468 if (item && SP_IS_RECT(item)) {
2469 {
2470 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2471 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2472 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2473 }
2475 {
2476 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2477 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2478 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2479 }
2481 {
2482 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2483 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2484 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2485 }
2487 {
2488 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2489 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2490 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2491 }
2492 }
2494 sp_rtb_sensitivize( tbl );
2496 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2497 }
2500 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2501 NULL, /* child_added */
2502 NULL, /* child_removed */
2503 rect_tb_event_attr_changed,
2504 NULL, /* content_changed */
2505 NULL /* order_changed */
2506 };
2508 /**
2509 * \param selection should not be NULL.
2510 */
2511 static void
2512 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2513 {
2514 int n_selected = 0;
2515 Inkscape::XML::Node *repr = NULL;
2516 SPItem *item = NULL;
2518 if ( g_object_get_data( tbl, "repr" ) ) {
2519 g_object_set_data( tbl, "item", NULL );
2520 }
2521 purge_repr_listener( tbl, tbl );
2523 for (GSList const *items = selection->itemList();
2524 items != NULL;
2525 items = items->next) {
2526 if (SP_IS_RECT((SPItem *) items->data)) {
2527 n_selected++;
2528 item = (SPItem *) items->data;
2529 repr = SP_OBJECT_REPR(item);
2530 }
2531 }
2533 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2535 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2537 if (n_selected == 0) {
2538 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2540 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2541 gtk_action_set_sensitive(w, FALSE);
2542 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2543 gtk_action_set_sensitive(h, FALSE);
2545 } else if (n_selected == 1) {
2546 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2547 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2549 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2550 gtk_action_set_sensitive(w, TRUE);
2551 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2552 gtk_action_set_sensitive(h, TRUE);
2554 if (repr) {
2555 g_object_set_data( tbl, "repr", repr );
2556 g_object_set_data( tbl, "item", item );
2557 Inkscape::GC::anchor(repr);
2558 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2559 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2560 }
2561 } else {
2562 // FIXME: implement averaging of all parameters for multiple selected
2563 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2564 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2565 sp_rtb_sensitivize( tbl );
2566 }
2567 }
2570 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2571 {
2572 EgeAdjustmentAction* eact = 0;
2573 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2575 {
2576 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2577 ege_output_action_set_use_markup( act, TRUE );
2578 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2579 g_object_set_data( holder, "mode_action", act );
2580 }
2582 // rx/ry units menu: create
2583 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2584 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2585 // fixme: add % meaning per cent of the width/height
2586 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2587 g_object_set_data( holder, "tracker", tracker );
2589 /* W */
2590 {
2591 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2592 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2593 eact = create_adjustment_action( "RectWidthAction",
2594 _("Width"), _("W:"), _("Width of rectangle"),
2595 "tools.shapes.rect", "width", 0,
2596 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2597 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2598 labels, values, G_N_ELEMENTS(labels),
2599 sp_rtb_width_value_changed );
2600 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2601 g_object_set_data( holder, "width_action", eact );
2602 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2603 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2604 }
2606 /* H */
2607 {
2608 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2609 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2610 eact = create_adjustment_action( "RectHeightAction",
2611 _("Height"), _("H:"), _("Height of rectangle"),
2612 "tools.shapes.rect", "height", 0,
2613 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2614 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2615 labels, values, G_N_ELEMENTS(labels),
2616 sp_rtb_height_value_changed );
2617 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2618 g_object_set_data( holder, "height_action", eact );
2619 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2620 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2621 }
2623 /* rx */
2624 {
2625 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2626 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2627 eact = create_adjustment_action( "RadiusXAction",
2628 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2629 "tools.shapes.rect", "rx", 0,
2630 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2631 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2632 labels, values, G_N_ELEMENTS(labels),
2633 sp_rtb_rx_value_changed);
2634 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2635 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2636 }
2638 /* ry */
2639 {
2640 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2641 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2642 eact = create_adjustment_action( "RadiusYAction",
2643 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2644 "tools.shapes.rect", "ry", 0,
2645 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2646 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2647 labels, values, G_N_ELEMENTS(labels),
2648 sp_rtb_ry_value_changed);
2649 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2650 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2651 }
2653 // add the units menu
2654 {
2655 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2656 gtk_action_group_add_action( mainActions, act );
2657 }
2659 /* Reset */
2660 {
2661 InkAction* inky = ink_action_new( "RectResetAction",
2662 _("Not rounded"),
2663 _("Make corners sharp"),
2664 "squared_corner",
2665 secondarySize );
2666 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2667 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2668 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2669 g_object_set_data( holder, "not_rounded", inky );
2670 }
2672 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2673 sp_rtb_sensitivize( holder );
2675 sigc::connection *connection = new sigc::connection(
2676 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2677 );
2678 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2679 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2680 }
2682 //########################
2683 //## 3D Box ##
2684 //########################
2686 // normalize angle so that it lies in the interval [0,360]
2687 static double box3d_normalize_angle (double a) {
2688 double angle = a + ((int) (a/360.0))*360;
2689 if (angle < 0) {
2690 angle += 360.0;
2691 }
2692 return angle;
2693 }
2695 static void
2696 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2697 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2698 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2699 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2700 // are reset).
2701 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2703 if (is_infinite) {
2704 gtk_toggle_action_set_active(tact, TRUE);
2705 gtk_action_set_sensitive(act, TRUE);
2707 double angle = persp3d_get_infinite_angle(persp, axis);
2708 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2709 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2710 }
2711 } else {
2712 gtk_toggle_action_set_active(tact, FALSE);
2713 gtk_action_set_sensitive(act, FALSE);
2714 }
2715 }
2717 static void
2718 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2719 if (!persp_repr) {
2720 g_print ("No perspective given to box3d_resync_toolbar().\n");
2721 return;
2722 }
2724 GtkWidget *tbl = GTK_WIDGET(data);
2725 GtkAdjustment *adj = 0;
2726 GtkAction *act = 0;
2727 GtkToggleAction *tact = 0;
2728 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2729 {
2730 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2731 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2732 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2734 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2735 }
2736 {
2737 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2738 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2739 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2741 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2742 }
2743 {
2744 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2745 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2746 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2748 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2749 }
2750 }
2752 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2753 gchar const */*old_value*/, gchar const */*new_value*/,
2754 bool /*is_interactive*/, gpointer data)
2755 {
2756 GtkWidget *tbl = GTK_WIDGET(data);
2758 // quit if run by the attr_changed listener
2759 // note: it used to work without the differently called freeze_ attributes (here and in
2760 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2761 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2762 return;
2763 }
2765 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2766 // sp_document_maybe_done() when the document is undo insensitive)
2767 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2769 // TODO: Only update the appropriate part of the toolbar
2770 // if (!strcmp(name, "inkscape:vp_z")) {
2771 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2772 // }
2774 Persp3D *persp = persp3d_get_from_repr(repr);
2775 persp3d_update_box_reprs(persp);
2777 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2778 }
2780 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2781 {
2782 NULL, /* child_added */
2783 NULL, /* child_removed */
2784 box3d_persp_tb_event_attr_changed,
2785 NULL, /* content_changed */
2786 NULL /* order_changed */
2787 };
2789 /**
2790 * \param selection Should not be NULL.
2791 */
2792 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2793 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2794 static void
2795 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2796 {
2797 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2798 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2799 // update the perspectives with infinite VPs and leave the other ones untouched).
2801 Inkscape::XML::Node *persp_repr = NULL;
2802 purge_repr_listener(tbl, tbl);
2804 SPItem *item = selection->singleItem();
2805 if (item && SP_IS_BOX3D(item)) {
2806 // FIXME: Also deal with multiple selected boxes
2807 SPBox3D *box = SP_BOX3D(item);
2808 Persp3D *persp = box3d_get_perspective(box);
2809 persp_repr = SP_OBJECT_REPR(persp);
2810 if (persp_repr) {
2811 g_object_set_data(tbl, "repr", persp_repr);
2812 Inkscape::GC::anchor(persp_repr);
2813 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2814 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2815 }
2817 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2818 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2820 box3d_resync_toolbar(persp_repr, tbl);
2821 }
2822 }
2824 static void
2825 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2826 {
2827 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2828 SPDocument *document = sp_desktop_document(desktop);
2830 // quit if run by the attr_changed listener
2831 // note: it used to work without the differently called freeze_ attributes (here and in
2832 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2833 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2834 return;
2835 }
2837 // in turn, prevent listener from responding
2838 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2840 //Persp3D *persp = document->current_persp3d;
2841 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2842 if (sel_persps.empty()) {
2843 // this can happen when the document is created; we silently ignore it
2844 return;
2845 }
2846 Persp3D *persp = sel_persps.front();
2848 persp->tmat.set_infinite_direction (axis, adj->value);
2849 SP_OBJECT(persp)->updateRepr();
2851 // TODO: use the correct axis here, too
2852 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2854 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2855 }
2858 static void
2859 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2860 {
2861 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2862 }
2864 static void
2865 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2866 {
2867 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2868 }
2870 static void
2871 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2872 {
2873 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2874 }
2877 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2878 {
2879 // TODO: Take all selected perspectives into account
2880 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2881 if (sel_persps.empty()) {
2882 // this can happen when the document is created; we silently ignore it
2883 return;
2884 }
2885 Persp3D *persp = sel_persps.front();
2887 bool set_infinite = gtk_toggle_action_get_active(act);
2888 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2889 }
2891 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2892 {
2893 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2894 }
2896 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2897 {
2898 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2899 }
2901 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2902 {
2903 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2904 }
2906 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2907 {
2908 EgeAdjustmentAction* eact = 0;
2909 SPDocument *document = sp_desktop_document (desktop);
2910 Persp3D *persp = document->current_persp3d;
2912 EgeAdjustmentAction* box3d_angle_x = 0;
2913 EgeAdjustmentAction* box3d_angle_y = 0;
2914 EgeAdjustmentAction* box3d_angle_z = 0;
2916 /* Angle X */
2917 {
2918 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2919 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2920 eact = create_adjustment_action( "3DBoxAngleXAction",
2921 _("Angle in X direction"), _("Angle X:"),
2922 // Translators: PL is short for 'perspective line'
2923 _("Angle of PLs in X direction"),
2924 "tools.shapes.3dbox", "box3d_angle_x", 30,
2925 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2926 -360.0, 360.0, 1.0, 10.0,
2927 labels, values, G_N_ELEMENTS(labels),
2928 box3d_angle_x_value_changed );
2929 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2930 g_object_set_data( holder, "box3d_angle_x_action", eact );
2931 box3d_angle_x = eact;
2932 }
2934 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2935 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2936 } else {
2937 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2938 }
2941 /* VP X state */
2942 {
2943 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2944 // Translators: VP is short for 'vanishing point'
2945 _("State of VP in X direction"),
2946 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2947 "toggle_vp_x",
2948 Inkscape::ICON_SIZE_DECORATION );
2949 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2950 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2951 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2952 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2953 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2954 }
2956 /* Angle Y */
2957 {
2958 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2959 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2960 eact = create_adjustment_action( "3DBoxAngleYAction",
2961 _("Angle in Y direction"), _("Angle Y:"),
2962 // Translators: PL is short for 'perspective line'
2963 _("Angle of PLs in Y direction"),
2964 "tools.shapes.3dbox", "box3d_angle_y", 30,
2965 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2966 -360.0, 360.0, 1.0, 10.0,
2967 labels, values, G_N_ELEMENTS(labels),
2968 box3d_angle_y_value_changed );
2969 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2970 g_object_set_data( holder, "box3d_angle_y_action", eact );
2971 box3d_angle_y = eact;
2972 }
2974 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2975 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2976 } else {
2977 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2978 }
2980 /* VP Y state */
2981 {
2982 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2983 // Translators: VP is short for 'vanishing point'
2984 _("State of VP in Y direction"),
2985 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2986 "toggle_vp_y",
2987 Inkscape::ICON_SIZE_DECORATION );
2988 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2989 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2990 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2991 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2992 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2993 }
2995 /* Angle Z */
2996 {
2997 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2998 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2999 eact = create_adjustment_action( "3DBoxAngleZAction",
3000 _("Angle in Z direction"), _("Angle Z:"),
3001 // Translators: PL is short for 'perspective line'
3002 _("Angle of PLs in Z direction"),
3003 "tools.shapes.3dbox", "box3d_angle_z", 30,
3004 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3005 -360.0, 360.0, 1.0, 10.0,
3006 labels, values, G_N_ELEMENTS(labels),
3007 box3d_angle_z_value_changed );
3008 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3009 g_object_set_data( holder, "box3d_angle_z_action", eact );
3010 box3d_angle_z = eact;
3011 }
3013 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3014 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3015 } else {
3016 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3017 }
3019 /* VP Z state */
3020 {
3021 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3022 // Translators: VP is short for 'vanishing point'
3023 _("State of VP in Z direction"),
3024 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3025 "toggle_vp_z",
3026 Inkscape::ICON_SIZE_DECORATION );
3027 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3028 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3029 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3030 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3031 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3032 }
3034 sigc::connection *connection = new sigc::connection(
3035 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3036 );
3037 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3038 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3039 }
3041 //########################
3042 //## Spiral ##
3043 //########################
3045 static void
3046 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3047 {
3048 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3050 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3051 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3052 }
3054 // quit if run by the attr_changed listener
3055 if (g_object_get_data( tbl, "freeze" )) {
3056 return;
3057 }
3059 // in turn, prevent listener from responding
3060 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3062 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3064 bool modmade = false;
3065 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3066 items != NULL;
3067 items = items->next)
3068 {
3069 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3070 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3071 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3072 SP_OBJECT((SPItem *) items->data)->updateRepr();
3073 modmade = true;
3074 }
3075 }
3077 g_free(namespaced_name);
3079 if (modmade) {
3080 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3081 _("Change spiral"));
3082 }
3084 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3085 }
3087 static void
3088 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3089 {
3090 sp_spl_tb_value_changed(adj, tbl, "revolution");
3091 }
3093 static void
3094 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3095 {
3096 sp_spl_tb_value_changed(adj, tbl, "expansion");
3097 }
3099 static void
3100 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3101 {
3102 sp_spl_tb_value_changed(adj, tbl, "t0");
3103 }
3105 static void
3106 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3107 {
3108 GtkWidget *tbl = GTK_WIDGET(obj);
3110 GtkAdjustment *adj;
3112 // fixme: make settable
3113 gdouble rev = 5;
3114 gdouble exp = 1.0;
3115 gdouble t0 = 0.0;
3117 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3118 gtk_adjustment_set_value(adj, rev);
3119 gtk_adjustment_value_changed(adj);
3121 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3122 gtk_adjustment_set_value(adj, exp);
3123 gtk_adjustment_value_changed(adj);
3125 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3126 gtk_adjustment_set_value(adj, t0);
3127 gtk_adjustment_value_changed(adj);
3129 spinbutton_defocus(GTK_OBJECT(tbl));
3130 }
3133 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3134 gchar const */*old_value*/, gchar const */*new_value*/,
3135 bool /*is_interactive*/, gpointer data)
3136 {
3137 GtkWidget *tbl = GTK_WIDGET(data);
3139 // quit if run by the _changed callbacks
3140 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3141 return;
3142 }
3144 // in turn, prevent callbacks from responding
3145 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3147 GtkAdjustment *adj;
3148 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3149 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3151 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3152 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3154 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3155 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3157 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3158 }
3161 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3162 NULL, /* child_added */
3163 NULL, /* child_removed */
3164 spiral_tb_event_attr_changed,
3165 NULL, /* content_changed */
3166 NULL /* order_changed */
3167 };
3169 static void
3170 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3171 {
3172 int n_selected = 0;
3173 Inkscape::XML::Node *repr = NULL;
3175 purge_repr_listener( tbl, tbl );
3177 for (GSList const *items = selection->itemList();
3178 items != NULL;
3179 items = items->next)
3180 {
3181 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3182 n_selected++;
3183 repr = SP_OBJECT_REPR((SPItem *) items->data);
3184 }
3185 }
3187 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3189 if (n_selected == 0) {
3190 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3191 } else if (n_selected == 1) {
3192 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3194 if (repr) {
3195 g_object_set_data( tbl, "repr", repr );
3196 Inkscape::GC::anchor(repr);
3197 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3198 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3199 }
3200 } else {
3201 // FIXME: implement averaging of all parameters for multiple selected
3202 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3203 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3204 }
3205 }
3208 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3209 {
3210 EgeAdjustmentAction* eact = 0;
3211 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3213 {
3214 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3215 ege_output_action_set_use_markup( act, TRUE );
3216 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3217 g_object_set_data( holder, "mode_action", act );
3218 }
3220 /* Revolution */
3221 {
3222 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3223 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3224 eact = create_adjustment_action( "SpiralRevolutionAction",
3225 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3226 "tools.shapes.spiral", "revolution", 3.0,
3227 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3228 0.01, 1024.0, 0.1, 1.0,
3229 labels, values, G_N_ELEMENTS(labels),
3230 sp_spl_tb_revolution_value_changed, 1, 2);
3231 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3232 }
3234 /* Expansion */
3235 {
3236 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3237 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3238 eact = create_adjustment_action( "SpiralExpansionAction",
3239 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3240 "tools.shapes.spiral", "expansion", 1.0,
3241 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3242 0.0, 1000.0, 0.01, 1.0,
3243 labels, values, G_N_ELEMENTS(labels),
3244 sp_spl_tb_expansion_value_changed);
3245 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3246 }
3248 /* T0 */
3249 {
3250 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3251 gdouble values[] = {0, 0.5, 0.9};
3252 eact = create_adjustment_action( "SpiralT0Action",
3253 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3254 "tools.shapes.spiral", "t0", 0.0,
3255 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3256 0.0, 0.999, 0.01, 1.0,
3257 labels, values, G_N_ELEMENTS(labels),
3258 sp_spl_tb_t0_value_changed);
3259 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3260 }
3262 /* Reset */
3263 {
3264 InkAction* inky = ink_action_new( "SpiralResetAction",
3265 _("Defaults"),
3266 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3267 GTK_STOCK_CLEAR,
3268 secondarySize );
3269 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3270 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3271 }
3274 sigc::connection *connection = new sigc::connection(
3275 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3276 );
3277 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3278 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3279 }
3281 //########################
3282 //## Pen/Pencil ##
3283 //########################
3285 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3286 static char const *
3287 freehand_tool_name(GObject *dataKludge)
3288 {
3289 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3290 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3291 ? "tools.freehand.pen"
3292 : "tools.freehand.pencil" );
3293 }
3295 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3296 {
3297 gint mode = ege_select_one_action_get_active(act);
3299 prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3301 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3303 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3304 // preparatory work here
3305 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3306 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3307 sp_pen_context_set_polyline_mode(pc);
3308 }
3309 }
3311 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3312 {
3313 /* Freehand mode toggle buttons */
3314 {
3315 guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3316 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3318 {
3319 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3321 GtkTreeIter iter;
3322 gtk_list_store_append( model, &iter );
3323 gtk_list_store_set( model, &iter,
3324 0, _("Bezier"),
3325 1, _("Create regular Bezier path"),
3326 2, "bezier_mode",
3327 -1 );
3329 gtk_list_store_append( model, &iter );
3330 gtk_list_store_set( model, &iter,
3331 0, _("Spiro"),
3332 1, _("Create Spiro path"),
3333 2, "spiro_splines_mode",
3334 -1 );
3336 if (!tool_is_pencil) {
3337 gtk_list_store_append( model, &iter );
3338 gtk_list_store_set( model, &iter,
3339 0, _("Zigzag"),
3340 1, _("Create a sequence of straight line segments"),
3341 2, "polylines_mode",
3342 -1 );
3344 gtk_list_store_append( model, &iter );
3345 gtk_list_store_set( model, &iter,
3346 0, _("Paraxial"),
3347 1, _("Create a sequence of paraxial line segments"),
3348 2, "paraxial_lines_mode",
3349 -1 );
3350 }
3352 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3353 "FreehandModeActionPencil" :
3354 "FreehandModeActionPen",
3355 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3356 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3358 ege_select_one_action_set_appearance( act, "full" );
3359 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3360 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3361 ege_select_one_action_set_icon_column( act, 2 );
3362 ege_select_one_action_set_icon_size( act, secondarySize );
3363 ege_select_one_action_set_tooltip_column( act, 1 );
3365 ege_select_one_action_set_active( act, freehandMode);
3366 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3367 }
3368 }
3369 }
3371 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3372 gint shape = ege_select_one_action_get_active( act );
3373 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3374 }
3376 /**
3377 * \brief Generate the list of freehand advanced shape option entries.
3378 */
3379 GList * freehand_shape_dropdown_items_list() {
3380 GList *glist = NULL;
3382 glist = g_list_append (glist, _("None"));
3383 glist = g_list_append (glist, _("Triangle in"));
3384 glist = g_list_append (glist, _("Triangle out"));
3385 glist = g_list_append (glist, _("Ellipse"));
3386 glist = g_list_append (glist, _("From clipboard"));
3388 return glist;
3389 }
3391 static void
3392 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3393 /*advanced shape options */
3394 {
3395 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3397 GList* items = 0;
3398 gint count = 0;
3399 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3400 {
3401 GtkTreeIter iter;
3402 gtk_list_store_append( model, &iter );
3403 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3404 count++;
3405 }
3406 g_list_free( items );
3407 items = 0;
3408 EgeSelectOneAction* act1 = ege_select_one_action_new(
3409 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3410 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3411 g_object_set( act1, "short_label", _("Shape:"), NULL );
3412 ege_select_one_action_set_appearance( act1, "compact" );
3413 ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3414 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3415 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3416 g_object_set_data( holder, "shape_action", act1 );
3417 }
3418 }
3420 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3421 {
3422 sp_add_freehand_mode_toggle(mainActions, holder, false);
3423 freehand_add_advanced_shape_options(mainActions, holder, false);
3424 }
3427 static void
3428 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3429 {
3430 GtkWidget *tbl = GTK_WIDGET(obj);
3432 GtkAdjustment *adj;
3434 // fixme: make settable
3435 gdouble tolerance = 4;
3437 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3438 gtk_adjustment_set_value(adj, tolerance);
3439 gtk_adjustment_value_changed(adj);
3441 spinbutton_defocus(GTK_OBJECT(tbl));
3442 }
3444 static void
3445 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3446 {
3448 // quit if run by the attr_changed listener
3449 if (g_object_get_data( tbl, "freeze" )) {
3450 return;
3451 }
3452 // in turn, prevent listener from responding
3453 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3454 prefs_set_double_attribute("tools.freehand.pencil",
3455 "tolerance", adj->value);
3456 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3458 }
3462 static void
3463 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3464 const gchar */*key*/,
3465 const gchar */*oldval*/,
3466 const gchar */*newval*/,
3467 bool /*is_interactive*/,
3468 void * data)
3469 {
3470 GObject* tbl = G_OBJECT(data);
3471 if (g_object_get_data( tbl, "freeze" )) {
3472 return;
3473 }
3475 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3477 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3478 "tolerance");
3480 double v = prefs_get_double_attribute("tools.freehand.pencil",
3481 "tolerance", adj->value);
3482 gtk_adjustment_set_value(adj, v);
3483 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3485 }
3487 static Inkscape::XML::NodeEventVector pencil_node_events =
3488 {
3489 NULL,
3490 NULL,
3491 sp_pencil_tb_tolerance_value_changed_external,
3492 NULL,
3493 NULL,
3494 };
3497 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3498 {
3499 sp_add_freehand_mode_toggle(mainActions, holder, true);
3501 EgeAdjustmentAction* eact = 0;
3503 /* Tolerance */
3504 {
3505 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3506 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3507 eact = create_adjustment_action( "PencilToleranceAction",
3508 _("Smoothing:"), _("Smoothing: "),
3509 _("How much smoothing (simplifying) is applied to the line"),
3510 "tools.freehand.pencil", "tolerance",
3511 3.0,
3512 GTK_WIDGET(desktop->canvas), NULL,
3513 holder, TRUE, "altx-pencil",
3514 1, 100.0, 0.5, 0,
3515 labels, values, G_N_ELEMENTS(labels),
3516 sp_pencil_tb_tolerance_value_changed,
3517 1, 2);
3518 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3519 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3521 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3522 "tools.freehand.pencil");
3523 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3524 g_object_set_data(G_OBJECT(holder), "repr", repr);
3526 }
3528 /* advanced shape options */
3529 freehand_add_advanced_shape_options(mainActions, holder, true);
3531 /* Reset */
3532 {
3533 InkAction* inky = ink_action_new( "PencilResetAction",
3534 _("Defaults"),
3535 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3536 GTK_STOCK_CLEAR,
3537 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3538 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3539 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3540 }
3542 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3544 }
3547 //########################
3548 //## Tweak ##
3549 //########################
3551 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3552 {
3553 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3554 }
3556 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3557 {
3558 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3559 }
3561 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3562 {
3563 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3564 }
3566 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3567 {
3568 int mode = ege_select_one_action_get_active( act );
3569 prefs_set_int_attribute("tools.tweak", "mode", mode);
3571 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3572 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3573 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3574 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3575 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3576 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3577 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3578 if (doh) gtk_action_set_sensitive (doh, TRUE);
3579 if (dos) gtk_action_set_sensitive (dos, TRUE);
3580 if (dol) gtk_action_set_sensitive (dol, TRUE);
3581 if (doo) gtk_action_set_sensitive (doo, TRUE);
3582 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3583 if (fid) gtk_action_set_sensitive (fid, FALSE);
3584 } else {
3585 if (doh) gtk_action_set_sensitive (doh, FALSE);
3586 if (dos) gtk_action_set_sensitive (dos, FALSE);
3587 if (dol) gtk_action_set_sensitive (dol, FALSE);
3588 if (doo) gtk_action_set_sensitive (doo, FALSE);
3589 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3590 if (fid) gtk_action_set_sensitive (fid, TRUE);
3591 }
3592 }
3594 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3595 {
3596 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3597 }
3599 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3600 bool show = gtk_toggle_action_get_active( act );
3601 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3602 }
3603 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3604 bool show = gtk_toggle_action_get_active( act );
3605 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3606 }
3607 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3608 bool show = gtk_toggle_action_get_active( act );
3609 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3610 }
3611 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3612 bool show = gtk_toggle_action_get_active( act );
3613 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3614 }
3616 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3617 {
3618 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3620 {
3621 /* Width */
3622 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3623 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3624 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3625 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3626 "tools.tweak", "width", 15,
3627 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3628 1, 100, 1.0, 0.0,
3629 labels, values, G_N_ELEMENTS(labels),
3630 sp_tweak_width_value_changed, 0.01, 0, 100 );
3631 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3632 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3633 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3634 }
3637 {
3638 /* Force */
3639 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3640 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3641 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3642 _("Force"), _("Force:"), _("The force of the tweak action"),
3643 "tools.tweak", "force", 20,
3644 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3645 1, 100, 1.0, 0.0,
3646 labels, values, G_N_ELEMENTS(labels),
3647 sp_tweak_force_value_changed, 0.01, 0, 100 );
3648 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3649 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3650 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3651 }
3653 /* Mode */
3654 {
3655 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3657 GtkTreeIter iter;
3658 gtk_list_store_append( model, &iter );
3659 gtk_list_store_set( model, &iter,
3660 0, _("Push mode"),
3661 1, _("Push parts of paths in any direction"),
3662 2, "tweak_push_mode",
3663 -1 );
3665 gtk_list_store_append( model, &iter );
3666 gtk_list_store_set( model, &iter,
3667 0, _("Shrink mode"),
3668 1, _("Shrink (inset) parts of paths"),
3669 2, "tweak_shrink_mode",
3670 -1 );
3672 gtk_list_store_append( model, &iter );
3673 gtk_list_store_set( model, &iter,
3674 0, _("Grow mode"),
3675 1, _("Grow (outset) parts of paths"),
3676 2, "tweak_grow_mode",
3677 -1 );
3679 gtk_list_store_append( model, &iter );
3680 gtk_list_store_set( model, &iter,
3681 0, _("Attract mode"),
3682 1, _("Attract parts of paths towards cursor"),
3683 2, "tweak_attract_mode",
3684 -1 );
3686 gtk_list_store_append( model, &iter );
3687 gtk_list_store_set( model, &iter,
3688 0, _("Repel mode"),
3689 1, _("Repel parts of paths from cursor"),
3690 2, "tweak_repel_mode",
3691 -1 );
3693 gtk_list_store_append( model, &iter );
3694 gtk_list_store_set( model, &iter,
3695 0, _("Roughen mode"),
3696 1, _("Roughen parts of paths"),
3697 2, "tweak_roughen_mode",
3698 -1 );
3700 gtk_list_store_append( model, &iter );
3701 gtk_list_store_set( model, &iter,
3702 0, _("Color paint mode"),
3703 1, _("Paint the tool's color upon selected objects"),
3704 2, "tweak_colorpaint_mode",
3705 -1 );
3707 gtk_list_store_append( model, &iter );
3708 gtk_list_store_set( model, &iter,
3709 0, _("Color jitter mode"),
3710 1, _("Jitter the colors of selected objects"),
3711 2, "tweak_colorjitter_mode",
3712 -1 );
3714 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3715 g_object_set( act, "short_label", _("Mode:"), NULL );
3716 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3717 g_object_set_data( holder, "mode_action", act );
3719 ege_select_one_action_set_appearance( act, "full" );
3720 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3721 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3722 ege_select_one_action_set_icon_column( act, 2 );
3723 ege_select_one_action_set_icon_size( act, secondarySize );
3724 ege_select_one_action_set_tooltip_column( act, 1 );
3726 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3727 ege_select_one_action_set_active( act, mode );
3728 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3730 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3731 }
3733 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3735 {
3736 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3737 ege_output_action_set_use_markup( act, TRUE );
3738 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3739 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3740 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3741 g_object_set_data( holder, "tweak_channels_label", act);
3742 }
3744 {
3745 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3746 _("Hue"),
3747 _("In color mode, act on objects' hue"),
3748 NULL,
3749 Inkscape::ICON_SIZE_DECORATION );
3750 //TRANSLATORS: "H" here stands for hue
3751 g_object_set( act, "short_label", _("H"), NULL );
3752 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3753 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3754 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3755 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3756 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3757 g_object_set_data( holder, "tweak_doh", act);
3758 }
3759 {
3760 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3761 _("Saturation"),
3762 _("In color mode, act on objects' saturation"),
3763 NULL,
3764 Inkscape::ICON_SIZE_DECORATION );
3765 //TRANSLATORS: "S" here stands for Saturation
3766 g_object_set( act, "short_label", _("S"), NULL );
3767 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3768 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3769 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3770 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3771 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3772 g_object_set_data( holder, "tweak_dos", act );
3773 }
3774 {
3775 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3776 _("Lightness"),
3777 _("In color mode, act on objects' lightness"),
3778 NULL,
3779 Inkscape::ICON_SIZE_DECORATION );
3780 //TRANSLATORS: "L" here stands for Lightness
3781 g_object_set( act, "short_label", _("L"), NULL );
3782 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3783 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3784 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3785 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3786 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3787 g_object_set_data( holder, "tweak_dol", act );
3788 }
3789 {
3790 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3791 _("Opacity"),
3792 _("In color mode, act on objects' opacity"),
3793 NULL,
3794 Inkscape::ICON_SIZE_DECORATION );
3795 //TRANSLATORS: "O" here stands for Opacity
3796 g_object_set( act, "short_label", _("O"), NULL );
3797 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3798 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3799 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3800 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3801 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3802 g_object_set_data( holder, "tweak_doo", act );
3803 }
3805 { /* Fidelity */
3806 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3807 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3808 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3809 _("Fidelity"), _("Fidelity:"),
3810 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3811 "tools.tweak", "fidelity", 50,
3812 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3813 1, 100, 1.0, 10.0,
3814 labels, values, G_N_ELEMENTS(labels),
3815 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3816 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3817 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3818 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3819 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3820 g_object_set_data( holder, "tweak_fidelity", eact );
3821 }
3824 /* Use Pressure button */
3825 {
3826 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3827 _("Pressure"),
3828 _("Use the pressure of the input device to alter the force of tweak action"),
3829 "use_pressure",
3830 Inkscape::ICON_SIZE_DECORATION );
3831 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3832 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3833 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3834 }
3836 }
3839 //########################
3840 //## Calligraphy ##
3841 //########################
3842 static void update_presets_list (GObject *tbl)
3843 {
3844 if (g_object_get_data(tbl, "presets_blocked"))
3845 return;
3847 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3848 if (!sel) {
3849 ege_select_one_action_set_active(sel, 0);
3850 return;
3851 }
3853 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3855 for (int i = 1; i <= total_prefs; i++) {
3856 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3857 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3859 bool match = true;
3861 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3862 iter;
3863 ++iter ) {
3864 const gchar *attr_name = g_quark_to_string(iter->key);
3865 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3866 continue;
3867 void *widget = g_object_get_data(tbl, attr_name);
3868 if (widget) {
3869 if (GTK_IS_ADJUSTMENT(widget)) {
3870 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3871 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3872 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3873 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3874 match = false;
3875 break;
3876 }
3877 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3878 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3879 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3880 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3881 if (gtk_toggle_action_get_active(toggle) != v) {
3882 match = false;
3883 break;
3884 }
3885 }
3886 }
3887 }
3889 if (match) {
3890 // newly added item is at the same index as the
3891 // save command, so we need to change twice for it to take effect
3892 ege_select_one_action_set_active(sel, 0);
3893 ege_select_one_action_set_active(sel, i);
3894 return;
3895 }
3896 }
3898 // no match found
3899 ege_select_one_action_set_active(sel, 0);
3900 }
3902 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3903 {
3904 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3905 update_presets_list(tbl);
3906 }
3908 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3909 {
3910 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3911 update_presets_list(tbl);
3912 }
3914 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3915 {
3916 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3917 update_presets_list(tbl);
3918 }
3920 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3921 {
3922 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3923 update_presets_list(tbl);
3924 }
3926 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3927 {
3928 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3929 update_presets_list(tbl);
3930 }
3932 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3933 {
3934 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3935 update_presets_list(tbl);
3936 }
3938 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3939 {
3940 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3941 update_presets_list(tbl);
3942 }
3944 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3945 {
3946 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3947 update_presets_list(tbl);
3948 }
3950 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3951 {
3952 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3953 update_presets_list(tbl);
3954 }
3956 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3957 {
3958 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3959 update_presets_list(tbl);
3960 }
3962 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3963 {
3964 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3965 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3966 update_presets_list(tbl);
3967 if (calligraphy_angle )
3968 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3969 }
3972 static gchar const *const widget_names[] = {
3973 "width",
3974 "mass",
3975 "wiggle",
3976 "angle",
3977 "thinning",
3978 "tremor",
3979 "flatness",
3980 "cap_rounding",
3981 "usepressure",
3982 "tracebackground",
3983 "usetilt"
3984 };
3987 static void sp_dcc_build_presets_list(GObject *tbl)
3988 {
3989 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3991 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3992 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3993 gtk_list_store_clear (model);
3995 {
3996 GtkTreeIter iter;
3997 gtk_list_store_append( model, &iter );
3998 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3999 }
4001 //TODO: switch back to prefs API
4002 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4003 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4004 int ii=1;
4005 while (child_repr) {
4006 GtkTreeIter iter;
4007 char *preset_name = (char *) child_repr->attribute("name");
4008 gtk_list_store_append( model, &iter );
4009 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4010 child_repr = sp_repr_next(child_repr);
4011 }
4013 {
4014 GtkTreeIter iter;
4015 gtk_list_store_append( model, &iter );
4016 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4017 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4018 }
4020 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4022 update_presets_list (tbl);
4023 }
4025 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4026 {
4027 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4028 if (! desktop) return;
4030 if (g_object_get_data(tbl, "presets_blocked"))
4031 return;
4033 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4034 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4035 // dialog cancelled
4036 update_presets_list (tbl);
4037 return;
4038 }
4039 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4041 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4042 // empty name entered
4043 update_presets_list (tbl);
4044 return;
4045 }
4047 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4049 int new_index = -1;
4050 gchar *pref_path = NULL;
4051 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4053 for (int i = 1; i <= total_prefs; i++) {
4054 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4055 const gchar *name = prefs_get_string_attribute(path, "name");
4056 if (name && !strcmp(name, profile_name.c_str())) {
4057 // we already have preset with this name, replace its values
4058 new_index = i;
4059 pref_path = g_strdup(path);
4060 break;
4061 }
4062 }
4064 if (new_index == -1) {
4065 // no preset with this name, create
4066 new_index = total_prefs + 1;
4067 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4068 pref_path = create_pref("tools.calligraphic.preset", profile_id);
4069 g_free(profile_id);
4070 }
4072 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4073 gchar const *const widget_name = widget_names[i];
4074 void *widget = g_object_get_data(tbl, widget_name);
4075 if (widget) {
4076 if (GTK_IS_ADJUSTMENT(widget)) {
4077 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4078 double v = gtk_adjustment_get_value(adj);
4079 prefs_set_double_attribute(pref_path, widget_name, v);
4080 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4081 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4082 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4083 int v = gtk_toggle_action_get_active(toggle);
4084 prefs_set_int_attribute(pref_path, widget_name, v);
4085 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4086 } else {
4087 g_warning("Unknown widget type for preset: %s\n", widget_name);
4088 }
4089 } else {
4090 g_warning("Bad key when writing preset: %s\n", widget_name);
4091 }
4092 }
4093 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4095 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4097 sp_dcc_build_presets_list (tbl);
4099 g_free(pref_path);
4100 }
4103 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4105 gint preset_index = ege_select_one_action_get_active( act );
4106 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4108 if (preset_index == save_presets_index) {
4109 // this is the Save command
4110 sp_dcc_save_profile(NULL, tbl);
4111 return;
4112 }
4114 if (g_object_get_data(tbl, "presets_blocked"))
4115 return;
4117 gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4119 if (preset_path) {
4120 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
4122 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4124 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4125 iter;
4126 ++iter ) {
4127 const gchar *attr_name = g_quark_to_string(iter->key);
4128 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4129 continue;
4130 void *widget = g_object_get_data(tbl, attr_name);
4131 if (widget) {
4132 if (GTK_IS_ADJUSTMENT(widget)) {
4133 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4134 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4135 gtk_adjustment_set_value(adj, v);
4136 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4137 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4138 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4139 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4140 gtk_toggle_action_set_active(toggle, v);
4141 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4142 } else {
4143 g_warning("Unknown widget type for preset: %s\n", attr_name);
4144 }
4145 } else {
4146 g_warning("Bad key found in a preset record: %s\n", attr_name);
4147 }
4148 }
4149 g_free(preset_path);
4150 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4151 }
4153 }
4156 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4157 {
4158 {
4159 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4161 EgeAdjustmentAction* calligraphy_angle = 0;
4163 {
4164 /* Width */
4165 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4166 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4167 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4168 _("Pen Width"), _("Width:"),
4169 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4170 "tools.calligraphic", "width", 15,
4171 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4172 1, 100, 1.0, 0.0,
4173 labels, values, G_N_ELEMENTS(labels),
4174 sp_ddc_width_value_changed, 0.01, 0, 100 );
4175 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4176 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4177 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4178 }
4180 {
4181 /* Thinning */
4182 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4183 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4184 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4185 _("Stroke Thinning"), _("Thinning:"),
4186 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4187 "tools.calligraphic", "thinning", 10,
4188 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4189 -100, 100, 1, 0.1,
4190 labels, values, G_N_ELEMENTS(labels),
4191 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4192 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4193 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4194 }
4196 {
4197 /* Angle */
4198 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4199 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4200 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4201 _("Pen Angle"), _("Angle:"),
4202 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4203 "tools.calligraphic", "angle", 30,
4204 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4205 -90.0, 90.0, 1.0, 10.0,
4206 labels, values, G_N_ELEMENTS(labels),
4207 sp_ddc_angle_value_changed, 1, 0 );
4208 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4209 g_object_set_data( holder, "angle_action", eact );
4210 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4211 calligraphy_angle = eact;
4212 }
4214 {
4215 /* Fixation */
4216 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4217 gdouble values[] = {0, 20, 40, 60, 90, 100};
4218 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4219 _("Fixation"), _("Fixation:"),
4220 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4221 "tools.calligraphic", "flatness", 90,
4222 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4223 0.0, 100, 1.0, 10.0,
4224 labels, values, G_N_ELEMENTS(labels),
4225 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4226 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4227 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4228 }
4230 {
4231 /* Cap Rounding */
4232 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4233 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4234 // TRANSLATORS: "cap" means "end" (both start and finish) here
4235 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4236 _("Cap rounding"), _("Caps:"),
4237 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4238 "tools.calligraphic", "cap_rounding", 0.0,
4239 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4240 0.0, 5.0, 0.01, 0.1,
4241 labels, values, G_N_ELEMENTS(labels),
4242 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4243 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4244 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4245 }
4247 {
4248 /* Tremor */
4249 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4250 gdouble values[] = {0, 10, 20, 40, 60, 100};
4251 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4252 _("Stroke Tremor"), _("Tremor:"),
4253 _("Increase to make strokes rugged and trembling"),
4254 "tools.calligraphic", "tremor", 0.0,
4255 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4256 0.0, 100, 1, 0.0,
4257 labels, values, G_N_ELEMENTS(labels),
4258 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4260 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4261 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4262 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4263 }
4265 {
4266 /* Wiggle */
4267 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4268 gdouble values[] = {0, 20, 40, 60, 100};
4269 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4270 _("Pen Wiggle"), _("Wiggle:"),
4271 _("Increase to make the pen waver and wiggle"),
4272 "tools.calligraphic", "wiggle", 0.0,
4273 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4274 0.0, 100, 1, 0.0,
4275 labels, values, G_N_ELEMENTS(labels),
4276 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4277 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4278 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4279 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4280 }
4282 {
4283 /* Mass */
4284 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4285 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4286 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4287 _("Pen Mass"), _("Mass:"),
4288 _("Increase to make the pen drag behind, as if slowed by inertia"),
4289 "tools.calligraphic", "mass", 2.0,
4290 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4291 0.0, 100, 1, 0.0,
4292 labels, values, G_N_ELEMENTS(labels),
4293 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4294 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4295 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4296 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4297 }
4300 /* Trace Background button */
4301 {
4302 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4303 _("Trace Background"),
4304 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4305 "trace_background",
4306 Inkscape::ICON_SIZE_DECORATION );
4307 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4308 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4309 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4310 g_object_set_data( holder, "tracebackground", act );
4311 }
4313 /* Use Pressure button */
4314 {
4315 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4316 _("Pressure"),
4317 _("Use the pressure of the input device to alter the width of the pen"),
4318 "use_pressure",
4319 Inkscape::ICON_SIZE_DECORATION );
4320 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4321 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4322 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4323 g_object_set_data( holder, "usepressure", act );
4324 }
4326 /* Use Tilt button */
4327 {
4328 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4329 _("Tilt"),
4330 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4331 "use_tilt",
4332 Inkscape::ICON_SIZE_DECORATION );
4333 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4334 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4335 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4336 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4337 g_object_set_data( holder, "usetilt", act );
4338 }
4340 /*calligraphic profile */
4341 {
4342 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4343 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4344 ege_select_one_action_set_appearance (act1, "compact");
4345 g_object_set_data (holder, "profile_selector", act1 );
4347 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4349 sp_dcc_build_presets_list (holder);
4351 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4352 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4353 }
4354 }
4355 }
4358 //########################
4359 //## Circle / Arc ##
4360 //########################
4362 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4363 {
4364 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4365 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4367 if (v1 == 0 && v2 == 0) {
4368 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4369 gtk_action_set_sensitive( ocb, FALSE );
4370 gtk_action_set_sensitive( make_whole, FALSE );
4371 }
4372 } else {
4373 gtk_action_set_sensitive( ocb, TRUE );
4374 gtk_action_set_sensitive( make_whole, TRUE );
4375 }
4376 }
4378 static void
4379 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4380 {
4381 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4383 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4384 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4385 }
4387 // quit if run by the attr_changed listener
4388 if (g_object_get_data( tbl, "freeze" )) {
4389 return;
4390 }
4392 // in turn, prevent listener from responding
4393 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4395 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4397 bool modmade = false;
4398 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4399 items != NULL;
4400 items = items->next)
4401 {
4402 SPItem *item = SP_ITEM(items->data);
4404 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4406 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4407 SPArc *arc = SP_ARC(item);
4409 if (!strcmp(value_name, "start"))
4410 ge->start = (adj->value * M_PI)/ 180;
4411 else
4412 ge->end = (adj->value * M_PI)/ 180;
4414 sp_genericellipse_normalize(ge);
4415 ((SPObject *)arc)->updateRepr();
4416 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4418 modmade = true;
4419 }
4420 }
4422 g_free(namespaced_name);
4424 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4426 sp_arctb_sensitivize( tbl, adj->value, other->value );
4428 if (modmade) {
4429 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4430 _("Arc: Change start/end"));
4431 }
4433 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4434 }
4437 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4438 {
4439 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4440 }
4442 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4443 {
4444 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4445 }
4448 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4449 {
4450 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4451 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4452 if ( ege_select_one_action_get_active( act ) != 0 ) {
4453 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4454 } else {
4455 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4456 }
4457 }
4459 // quit if run by the attr_changed listener
4460 if (g_object_get_data( tbl, "freeze" )) {
4461 return;
4462 }
4464 // in turn, prevent listener from responding
4465 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4467 bool modmade = false;
4469 if ( ege_select_one_action_get_active(act) != 0 ) {
4470 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4471 items != NULL;
4472 items = items->next)
4473 {
4474 if (SP_IS_ARC((SPItem *) items->data)) {
4475 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4476 repr->setAttribute("sodipodi:open", "true");
4477 SP_OBJECT((SPItem *) items->data)->updateRepr();
4478 modmade = true;
4479 }
4480 }
4481 } else {
4482 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4483 items != NULL;
4484 items = items->next)
4485 {
4486 if (SP_IS_ARC((SPItem *) items->data)) {
4487 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4488 repr->setAttribute("sodipodi:open", NULL);
4489 SP_OBJECT((SPItem *) items->data)->updateRepr();
4490 modmade = true;
4491 }
4492 }
4493 }
4495 if (modmade) {
4496 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4497 _("Arc: Change open/closed"));
4498 }
4500 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4501 }
4503 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4504 {
4505 GtkAdjustment *adj;
4506 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4507 gtk_adjustment_set_value(adj, 0.0);
4508 gtk_adjustment_value_changed(adj);
4510 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4511 gtk_adjustment_set_value(adj, 0.0);
4512 gtk_adjustment_value_changed(adj);
4514 spinbutton_defocus( GTK_OBJECT(obj) );
4515 }
4517 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4518 gchar const */*old_value*/, gchar const */*new_value*/,
4519 bool /*is_interactive*/, gpointer data)
4520 {
4521 GObject *tbl = G_OBJECT(data);
4523 // quit if run by the _changed callbacks
4524 if (g_object_get_data( tbl, "freeze" )) {
4525 return;
4526 }
4528 // in turn, prevent callbacks from responding
4529 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4531 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4532 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4534 GtkAdjustment *adj1,*adj2;
4535 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4536 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4537 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4538 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4540 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4542 char const *openstr = NULL;
4543 openstr = repr->attribute("sodipodi:open");
4544 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4546 if (openstr) {
4547 ege_select_one_action_set_active( ocb, 1 );
4548 } else {
4549 ege_select_one_action_set_active( ocb, 0 );
4550 }
4552 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4553 }
4555 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4556 NULL, /* child_added */
4557 NULL, /* child_removed */
4558 arc_tb_event_attr_changed,
4559 NULL, /* content_changed */
4560 NULL /* order_changed */
4561 };
4564 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4565 {
4566 int n_selected = 0;
4567 Inkscape::XML::Node *repr = NULL;
4569 purge_repr_listener( tbl, tbl );
4571 for (GSList const *items = selection->itemList();
4572 items != NULL;
4573 items = items->next)
4574 {
4575 if (SP_IS_ARC((SPItem *) items->data)) {
4576 n_selected++;
4577 repr = SP_OBJECT_REPR((SPItem *) items->data);
4578 }
4579 }
4581 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4583 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4584 if (n_selected == 0) {
4585 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4586 } else if (n_selected == 1) {
4587 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4588 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4590 if (repr) {
4591 g_object_set_data( tbl, "repr", repr );
4592 Inkscape::GC::anchor(repr);
4593 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4594 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4595 }
4596 } else {
4597 // FIXME: implement averaging of all parameters for multiple selected
4598 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4599 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4600 sp_arctb_sensitivize( tbl, 1, 0 );
4601 }
4602 }
4605 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4606 {
4607 EgeAdjustmentAction* eact = 0;
4608 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4611 {
4612 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4613 ege_output_action_set_use_markup( act, TRUE );
4614 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4615 g_object_set_data( holder, "mode_action", act );
4616 }
4618 /* Start */
4619 {
4620 eact = create_adjustment_action( "ArcStartAction",
4621 _("Start"), _("Start:"),
4622 _("The angle (in degrees) from the horizontal to the arc's start point"),
4623 "tools.shapes.arc", "start", 0.0,
4624 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4625 -360.0, 360.0, 1.0, 10.0,
4626 0, 0, 0,
4627 sp_arctb_start_value_changed);
4628 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4629 }
4631 /* End */
4632 {
4633 eact = create_adjustment_action( "ArcEndAction",
4634 _("End"), _("End:"),
4635 _("The angle (in degrees) from the horizontal to the arc's end point"),
4636 "tools.shapes.arc", "end", 0.0,
4637 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4638 -360.0, 360.0, 1.0, 10.0,
4639 0, 0, 0,
4640 sp_arctb_end_value_changed);
4641 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4642 }
4644 /* Segments / Pie checkbox */
4645 {
4646 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4648 GtkTreeIter iter;
4649 gtk_list_store_append( model, &iter );
4650 gtk_list_store_set( model, &iter,
4651 0, _("Closed arc"),
4652 1, _("Switch to segment (closed shape with two radii)"),
4653 2, "circle_closed_arc",
4654 -1 );
4656 gtk_list_store_append( model, &iter );
4657 gtk_list_store_set( model, &iter,
4658 0, _("Open Arc"),
4659 1, _("Switch to arc (unclosed shape)"),
4660 2, "circle_open_arc",
4661 -1 );
4663 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4664 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4665 g_object_set_data( holder, "open_action", act );
4667 ege_select_one_action_set_appearance( act, "full" );
4668 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4669 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4670 ege_select_one_action_set_icon_column( act, 2 );
4671 ege_select_one_action_set_icon_size( act, secondarySize );
4672 ege_select_one_action_set_tooltip_column( act, 1 );
4674 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4675 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4676 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4677 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4678 }
4680 /* Make Whole */
4681 {
4682 InkAction* inky = ink_action_new( "ArcResetAction",
4683 _("Make whole"),
4684 _("Make the shape a whole ellipse, not arc or segment"),
4685 "reset_circle",
4686 secondarySize );
4687 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4688 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4689 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4690 g_object_set_data( holder, "make_whole", inky );
4691 }
4693 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4694 // sensitivize make whole and open checkbox
4695 {
4696 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4697 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4698 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4699 }
4702 sigc::connection *connection = new sigc::connection(
4703 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4704 );
4705 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4706 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4707 }
4712 // toggle button callbacks and updaters
4714 //########################
4715 //## Dropper ##
4716 //########################
4718 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4719 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4720 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4721 if ( set_action ) {
4722 if ( gtk_toggle_action_get_active( act ) ) {
4723 gtk_action_set_sensitive( set_action, TRUE );
4724 } else {
4725 gtk_action_set_sensitive( set_action, FALSE );
4726 }
4727 }
4729 spinbutton_defocus(GTK_OBJECT(tbl));
4730 }
4732 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4733 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4734 spinbutton_defocus(GTK_OBJECT(tbl));
4735 }
4738 /**
4739 * Dropper auxiliary toolbar construction and setup.
4740 *
4741 * TODO: Would like to add swatch of current color.
4742 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4743 * can drag and drop places. Will provide a nice mixing palette.
4744 */
4745 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4746 {
4747 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4749 {
4750 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4751 ege_output_action_set_use_markup( act, TRUE );
4752 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4753 }
4755 {
4756 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4757 _("Pick opacity"),
4758 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4759 NULL,
4760 Inkscape::ICON_SIZE_DECORATION );
4761 g_object_set( act, "short_label", _("Pick"), NULL );
4762 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4763 g_object_set_data( holder, "pick_action", act );
4764 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4765 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4766 }
4768 {
4769 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4770 _("Assign opacity"),
4771 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4772 NULL,
4773 Inkscape::ICON_SIZE_DECORATION );
4774 g_object_set( act, "short_label", _("Assign"), NULL );
4775 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4776 g_object_set_data( holder, "set_action", act );
4777 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4778 // make sure it's disabled if we're not picking alpha
4779 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4780 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4781 }
4782 }
4785 //########################
4786 //## LPETool ##
4787 //########################
4789 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4791 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4792 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4793 {
4794 using namespace Inkscape::LivePathEffect;
4796 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4798 // only take action if run by the attr_changed listener
4799 if (!g_object_get_data(tbl, "freeze")) {
4800 // in turn, prevent listener from responding
4801 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4803 // TODO: how can we set *all* actions inactive (such that no sutool is activated?)
4804 gint lpeToolMode = ege_select_one_action_get_active(act);
4805 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4806 prefs_set_int_attribute( "tools.lpetool", "mode", lpeToolMode );
4807 }
4809 EffectType type = lpesubtools[lpeToolMode];
4810 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4812 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4813 }
4814 }
4816 static void
4817 sp_lpetool_test_value_changed(GtkAdjustment *adj, GObject *tbl)
4818 {
4819 g_print ("sp_lpetool_test_value_changed()\n");
4820 using namespace Inkscape::LivePathEffect;
4822 // quit if run by the attr_changed listener
4823 if (g_object_get_data( tbl, "freeze" )) {
4824 return;
4825 }
4827 // in turn, prevent listener from responding
4828 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
4830 LPEAngleBisector *lpeab = static_cast<LPEAngleBisector *>(g_object_get_data(tbl, "currentlpe"));
4831 if (!lpeab) {
4832 g_print ("no LPE!\n");
4833 } else {
4834 g_print ("LPE found. Adjusting left length\n");
4835 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4836 lpeab->length_left.param_set_value(gtk_adjustment_get_value(adj));
4837 sp_lpe_item_update_patheffect(lpeitem, true, true);
4838 }
4840 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4841 }
4843 void
4844 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4845 {
4846 using namespace Inkscape::LivePathEffect;
4847 g_print ("sp_lpetool_toolbox_sel_changed()");
4848 {
4849 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "lpetool_test_action" ) );
4850 SPItem *item = selection->singleItem();
4851 if (item && SP_IS_LPE_ITEM(item)) {
4852 g_print (" - item found\n");
4853 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4854 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4855 if (lpe && lpe->effectType() == ANGLE_BISECTOR) {
4856 LPEAngleBisector *lpeab = dynamic_cast<LPEAngleBisector*>(lpe);
4857 g_object_set_data(tbl, "currentlpe", lpeab);
4858 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4859 gtk_action_set_sensitive(w, TRUE);
4860 } else {
4861 g_object_set_data(tbl, "currentlpe", NULL);
4862 g_object_set_data(tbl, "currentlpeitem", NULL);
4863 gtk_action_set_sensitive(w, FALSE);
4864 }
4865 } else {
4866 g_print (" - unsetting item\n");
4867 g_object_set_data(tbl, "currentlpe", NULL);
4868 g_object_set_data(tbl, "currentlpeitem", NULL);
4869 gtk_action_set_sensitive(w, FALSE);
4870 }
4871 }
4872 g_print ("\n");
4873 }
4875 static void
4876 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4877 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4879 bool show = gtk_toggle_action_get_active( act );
4880 prefs_set_int_attribute ("tools.lpetool", "show_bbox", show ? 1 : 0);
4882 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4883 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4884 lpetool_context_reset_limiting_bbox(lc);
4885 }
4886 }
4888 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4889 {
4890 /** Automatically create a list of LPEs that get added to the toolbar **/
4891 {
4892 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4894 GtkTreeIter iter;
4896 // the first toggle button represents the state that no subtool is active (remove this when
4897 // this can be modeled by EgeSelectOneAction or some other action)
4898 gtk_list_store_append( model, &iter );
4899 gtk_list_store_set( model, &iter,
4900 0, _("All inactive"),
4901 1, _("No geometric tool is active"),
4902 2, _("all_inactive"),
4903 -1 );
4905 Inkscape::LivePathEffect::EffectType type;
4906 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
4907 type = lpesubtools[i];
4908 gtk_list_store_append( model, &iter );
4909 gtk_list_store_set( model, &iter,
4910 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4911 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4912 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4913 -1 );
4914 }
4916 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4917 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4918 g_object_set_data( holder, "lpetool_mode_action", act );
4920 ege_select_one_action_set_appearance( act, "full" );
4921 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4922 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4923 ege_select_one_action_set_icon_column( act, 2 );
4924 ege_select_one_action_set_tooltip_column( act, 1 );
4926 gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4927 ege_select_one_action_set_active( act, lpeToolMode );
4928 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4929 }
4931 /* Show limiting bounding box */
4932 {
4933 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
4934 _("Show limiting bounding box"),
4935 _("Show bounding box (is used to cut infinite lines)"),
4936 "lpetool_show_bbox",
4937 Inkscape::ICON_SIZE_DECORATION );
4938 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4939 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
4940 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
4941 }
4943 /* Test action */
4944 /**
4945 {
4946 EgeAdjustmentAction* eact = 0;
4947 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
4948 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
4949 eact = create_adjustment_action( "TestLPEAction",
4950 _("Test value"), _("Test value:"), _("Test for interactive control widgets ..."),
4951 "tools.lpetool", "testvalue", 0,
4952 GTK_WIDGET(desktop->canvas), NULL us, holder, TRUE, "altx-lpetool",
4953 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
4954 labels, values, G_N_ELEMENTS(labels),
4955 sp_lpetool_test_value_changed );
4956 //tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4957 g_object_set_data( holder, "lpetool_test_action", eact );
4958 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
4959 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4960 }
4961 **/
4963 //watch selection
4964 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
4966 sigc::connection *c_selection_changed =
4967 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4968 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
4969 pool->add_connection ("selection-changed", c_selection_changed);
4970 }
4972 //########################
4973 //## Eraser ##
4974 //########################
4976 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4977 {
4978 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4979 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4980 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4981 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4982 }
4984 // only take action if run by the attr_changed listener
4985 if (!g_object_get_data( tbl, "freeze" )) {
4986 // in turn, prevent listener from responding
4987 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4989 if ( eraserMode != 0 ) {
4990 } else {
4991 }
4992 // TODO finish implementation
4994 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4995 }
4996 }
4998 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4999 {
5000 {
5001 /* Width */
5002 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5003 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5004 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5005 _("Pen Width"), _("Width:"),
5006 _("The width of the eraser pen (relative to the visible canvas area)"),
5007 "tools.eraser", "width", 15,
5008 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5009 1, 100, 1.0, 0.0,
5010 labels, values, G_N_ELEMENTS(labels),
5011 sp_ddc_width_value_changed, 0.01, 0, 100 );
5012 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5013 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5014 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5015 }
5017 {
5018 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5020 GtkTreeIter iter;
5021 gtk_list_store_append( model, &iter );
5022 gtk_list_store_set( model, &iter,
5023 0, _("Delete"),
5024 1, _("Delete objects touched by the eraser"),
5025 2, "delete_object",
5026 -1 );
5028 gtk_list_store_append( model, &iter );
5029 gtk_list_store_set( model, &iter,
5030 0, _("Cut"),
5031 1, _("Cut out from objects"),
5032 2, "difference",
5033 -1 );
5035 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5036 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5037 g_object_set_data( holder, "eraser_mode_action", act );
5039 ege_select_one_action_set_appearance( act, "full" );
5040 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5041 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5042 ege_select_one_action_set_icon_column( act, 2 );
5043 ege_select_one_action_set_tooltip_column( act, 1 );
5045 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5046 ege_select_one_action_set_active( act, eraserMode );
5047 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5048 }
5050 }
5052 //########################
5053 //## Text Toolbox ##
5054 //########################
5055 /*
5056 static void
5057 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5058 {
5059 //Call back for letter sizing spinbutton
5060 }
5062 static void
5063 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5064 {
5065 //Call back for line height spinbutton
5066 }
5068 static void
5069 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5070 {
5071 //Call back for horizontal kerning spinbutton
5072 }
5074 static void
5075 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5076 {
5077 //Call back for vertical kerning spinbutton
5078 }
5080 static void
5081 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5082 {
5083 //Call back for letter rotation spinbutton
5084 }*/
5086 namespace {
5088 bool popdown_visible = false;
5089 bool popdown_hasfocus = false;
5091 void
5092 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5093 {
5094 SPStyle *query =
5095 sp_style_new (SP_ACTIVE_DOCUMENT);
5097 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5099 int result_family =
5100 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5102 int result_style =
5103 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5105 int result_numbers =
5106 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5108 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5110 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5111 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5112 // there are no texts in selection, read from prefs
5114 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5115 if (repr) {
5116 sp_style_read_from_repr (query, repr);
5117 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5118 // do not reset the toolbar style from prefs if we already did it last time
5119 sp_style_unref(query);
5120 return;
5121 }
5122 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5123 } else {
5124 sp_style_unref(query);
5125 return;
5126 }
5127 } else {
5128 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5129 }
5131 if (query->text)
5132 {
5133 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5134 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5135 gtk_entry_set_text (GTK_ENTRY (entry), "");
5137 } else if (query->text->font_specification.value || query->text->font_family.value) {
5139 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5141 // Get the font that corresponds
5142 Glib::ustring familyName;
5144 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5145 if (font) {
5146 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5147 font->Unref();
5148 font = NULL;
5149 }
5151 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5153 Gtk::TreePath path;
5154 try {
5155 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5156 } catch (...) {
5157 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5158 sp_style_unref(query);
5159 return;
5160 }
5162 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5163 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5165 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5167 gtk_tree_selection_select_path (tselection, path.gobj());
5168 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5170 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5171 }
5173 //Size
5174 {
5175 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5176 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5177 g_object_set_data(tbl, "size-block", gpointer(1));
5178 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5179 g_object_set_data(tbl, "size-block", gpointer(0));
5180 g_free(str);
5181 }
5183 //Anchor
5184 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5185 {
5186 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5187 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5188 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5189 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5190 }
5191 else
5192 {
5193 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5194 {
5195 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5196 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5197 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5198 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5199 }
5200 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5201 {
5202 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5203 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5204 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5205 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5206 }
5207 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5208 {
5209 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5210 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5211 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5212 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5213 }
5214 }
5216 //Style
5217 {
5218 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5220 gboolean active = gtk_toggle_button_get_active (button);
5221 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5223 if (active != check)
5224 {
5225 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5226 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5227 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5228 }
5229 }
5231 {
5232 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5234 gboolean active = gtk_toggle_button_get_active (button);
5235 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5237 if (active != check)
5238 {
5239 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5240 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5241 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5242 }
5243 }
5245 //Orientation
5246 //locking both buttons, changing one affect all group (both)
5247 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5248 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5250 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5251 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5253 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5254 {
5255 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5256 }
5257 else
5258 {
5259 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5260 }
5261 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5262 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5263 }
5265 sp_style_unref(query);
5266 }
5268 void
5269 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5270 {
5271 sp_text_toolbox_selection_changed (selection, tbl);
5272 }
5274 void
5275 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5276 {
5277 sp_text_toolbox_selection_changed (NULL, tbl);
5278 }
5280 void
5281 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5282 GObject *tbl)
5283 {
5284 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5285 GtkTreeModel *model = 0;
5286 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5287 GtkTreeIter iter;
5288 char *family = 0;
5290 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5291 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5293 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5294 return;
5295 }
5297 gtk_tree_model_get (model, &iter, 0, &family, -1);
5299 if (g_object_get_data (G_OBJECT (selection), "block"))
5300 {
5301 gtk_entry_set_text (GTK_ENTRY (entry), family);
5302 return;
5303 }
5305 gtk_entry_set_text (GTK_ENTRY (entry), family);
5307 SPStyle *query =
5308 sp_style_new (SP_ACTIVE_DOCUMENT);
5310 int result_fontspec =
5311 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5313 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5315 SPCSSAttr *css = sp_repr_css_attr_new ();
5318 // First try to get the font spec from the stored value
5319 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5321 if (fontSpec.empty()) {
5322 // Construct a new font specification if it does not yet exist
5323 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5324 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5325 fontFromStyle->Unref();
5326 }
5328 if (!fontSpec.empty()) {
5329 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5330 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5331 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5332 if (font) {
5333 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5335 // Set all the these just in case they were altered when finding the best
5336 // match for the new family and old style...
5338 gchar c[256];
5340 font->Family(c, 256);
5341 sp_repr_css_set_property (css, "font-family", c);
5343 font->Attribute( "weight", c, 256);
5344 sp_repr_css_set_property (css, "font-weight", c);
5346 font->Attribute("style", c, 256);
5347 sp_repr_css_set_property (css, "font-style", c);
5349 font->Attribute("stretch", c, 256);
5350 sp_repr_css_set_property (css, "font-stretch", c);
5352 font->Attribute("variant", c, 256);
5353 sp_repr_css_set_property (css, "font-variant", c);
5355 font->Unref();
5356 }
5357 }
5358 }
5360 // If querying returned nothing, set the default style of the tool (for new texts)
5361 if (result_fontspec == QUERY_STYLE_NOTHING)
5362 {
5363 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5364 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5365 }
5366 else
5367 {
5368 sp_desktop_set_style (desktop, css, true, true);
5369 }
5371 sp_style_unref(query);
5373 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5374 _("Text: Change font family"));
5375 sp_repr_css_attr_unref (css);
5376 g_free(family);
5377 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5379 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5380 }
5382 /* This is where execution comes when the contents of the font family box have been completed
5383 by the press of the return key */
5384 void
5385 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5386 GObject *tbl)
5387 {
5388 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5390 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5391 try {
5392 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5393 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5394 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5395 gtk_tree_selection_select_path (selection, path.gobj());
5396 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5397 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5398 } catch (...) {
5399 if (family && strlen (family))
5400 {
5401 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5402 }
5403 }
5404 }
5406 void
5407 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5408 gpointer data)
5409 {
5410 if (g_object_get_data (G_OBJECT (button), "block")) return;
5411 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5412 int prop = GPOINTER_TO_INT(data);
5414 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5415 SPCSSAttr *css = sp_repr_css_attr_new ();
5417 switch (prop)
5418 {
5419 case 0:
5420 {
5421 sp_repr_css_set_property (css, "text-anchor", "start");
5422 sp_repr_css_set_property (css, "text-align", "start");
5423 break;
5424 }
5425 case 1:
5426 {
5427 sp_repr_css_set_property (css, "text-anchor", "middle");
5428 sp_repr_css_set_property (css, "text-align", "center");
5429 break;
5430 }
5432 case 2:
5433 {
5434 sp_repr_css_set_property (css, "text-anchor", "end");
5435 sp_repr_css_set_property (css, "text-align", "end");
5436 break;
5437 }
5439 case 3:
5440 {
5441 sp_repr_css_set_property (css, "text-anchor", "start");
5442 sp_repr_css_set_property (css, "text-align", "justify");
5443 break;
5444 }
5445 }
5447 SPStyle *query =
5448 sp_style_new (SP_ACTIVE_DOCUMENT);
5449 int result_numbers =
5450 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5452 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5453 if (result_numbers == QUERY_STYLE_NOTHING)
5454 {
5455 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5456 }
5458 sp_style_unref(query);
5460 sp_desktop_set_style (desktop, css, true, true);
5461 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5462 _("Text: Change alignment"));
5463 sp_repr_css_attr_unref (css);
5465 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5466 }
5468 void
5469 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5470 gpointer data)
5471 {
5472 if (g_object_get_data (G_OBJECT (button), "block")) return;
5474 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5475 SPCSSAttr *css = sp_repr_css_attr_new ();
5476 int prop = GPOINTER_TO_INT(data);
5477 bool active = gtk_toggle_button_get_active (button);
5479 SPStyle *query =
5480 sp_style_new (SP_ACTIVE_DOCUMENT);
5482 int result_fontspec =
5483 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5485 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5486 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5487 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5489 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5490 Glib::ustring newFontSpec = "";
5492 if (fontSpec.empty()) {
5493 // Construct a new font specification if it does not yet exist
5494 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5495 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5496 fontFromStyle->Unref();
5497 }
5499 switch (prop)
5500 {
5501 case 0:
5502 {
5503 if (!fontSpec.empty()) {
5504 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5505 }
5506 if (fontSpec != newFontSpec) {
5507 // Don't even set the bold if the font didn't exist on the system
5508 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5509 }
5510 break;
5511 }
5513 case 1:
5514 {
5515 if (!fontSpec.empty()) {
5516 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5517 }
5518 if (fontSpec != newFontSpec) {
5519 // Don't even set the italic if the font didn't exist on the system
5520 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5521 }
5522 break;
5523 }
5524 }
5526 if (!newFontSpec.empty()) {
5527 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5528 }
5530 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5531 if (result_fontspec == QUERY_STYLE_NOTHING)
5532 {
5533 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5534 }
5536 sp_style_unref(query);
5538 sp_desktop_set_style (desktop, css, true, true);
5539 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5540 _("Text: Change font style"));
5541 sp_repr_css_attr_unref (css);
5543 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5544 }
5546 void
5547 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5548 gpointer data)
5549 {
5550 if (g_object_get_data (G_OBJECT (button), "block")) {
5551 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5552 return;
5553 }
5555 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5556 SPCSSAttr *css = sp_repr_css_attr_new ();
5557 int prop = GPOINTER_TO_INT(data);
5559 switch (prop)
5560 {
5561 case 0:
5562 {
5563 sp_repr_css_set_property (css, "writing-mode", "lr");
5564 break;
5565 }
5567 case 1:
5568 {
5569 sp_repr_css_set_property (css, "writing-mode", "tb");
5570 break;
5571 }
5572 }
5574 SPStyle *query =
5575 sp_style_new (SP_ACTIVE_DOCUMENT);
5576 int result_numbers =
5577 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5579 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5580 if (result_numbers == QUERY_STYLE_NOTHING)
5581 {
5582 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5583 }
5585 sp_desktop_set_style (desktop, css, true, true);
5586 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5587 _("Text: Change orientation"));
5588 sp_repr_css_attr_unref (css);
5590 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5591 }
5593 gboolean
5594 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5595 {
5596 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5597 if (!desktop) return FALSE;
5599 switch (get_group0_keyval (event)) {
5600 case GDK_Escape: // defocus
5601 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5602 sp_text_toolbox_selection_changed (NULL, tbl); // update
5603 return TRUE; // I consumed the event
5604 break;
5605 }
5606 return FALSE;
5607 }
5609 gboolean
5610 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5611 {
5612 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5613 if (!desktop) return FALSE;
5615 switch (get_group0_keyval (event)) {
5616 case GDK_KP_Enter:
5617 case GDK_Return:
5618 case GDK_Escape: // defocus
5619 gtk_widget_hide (w);
5620 popdown_visible = false;
5621 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5622 return TRUE; // I consumed the event
5623 break;
5624 case GDK_w:
5625 case GDK_W:
5626 if (event->state & GDK_CONTROL_MASK) {
5627 gtk_widget_hide (w);
5628 popdown_visible = false;
5629 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5630 return TRUE; // I consumed the event
5631 }
5632 break;
5633 }
5634 return FALSE;
5635 }
5638 void
5639 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5640 GObject *tbl)
5641 {
5642 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5644 if (g_object_get_data (tbl, "size-block")) return;
5646 // If this is not from selecting a size in the list (in which case get_active will give the
5647 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5648 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5649 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5650 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5651 return;
5653 gdouble value = -1;
5654 {
5655 gchar *endptr;
5656 gchar *const text = gtk_combo_box_get_active_text(cbox);
5657 if (text) {
5658 value = g_strtod(text, &endptr);
5659 if (endptr == text) { // Conversion failed, non-numeric input.
5660 value = -1;
5661 }
5662 g_free(text);
5663 }
5664 }
5665 if (value <= 0) {
5666 return; // could not parse value
5667 }
5669 SPCSSAttr *css = sp_repr_css_attr_new ();
5670 Inkscape::CSSOStringStream osfs;
5671 osfs << value;
5672 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5674 SPStyle *query =
5675 sp_style_new (SP_ACTIVE_DOCUMENT);
5676 int result_numbers =
5677 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5679 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5680 if (result_numbers == QUERY_STYLE_NOTHING)
5681 {
5682 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5683 }
5685 sp_style_unref(query);
5687 sp_desktop_set_style (desktop, css, true, true);
5688 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5689 _("Text: Change font size"));
5690 sp_repr_css_attr_unref (css);
5692 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5693 }
5695 gboolean
5696 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5697 {
5698 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5699 if (!desktop) return FALSE;
5701 if (!g_object_get_data (tbl, "esc-pressed")) {
5702 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5703 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5704 sp_text_toolbox_size_changed (cbox, tbl);
5705 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5706 }
5707 return FALSE; // I consumed the event
5708 }
5711 gboolean
5712 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5713 {
5714 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5715 if (!desktop) return FALSE;
5717 switch (get_group0_keyval (event)) {
5718 case GDK_Escape: // defocus
5719 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5720 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5721 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5722 return TRUE; // I consumed the event
5723 break;
5724 case GDK_Return: // defocus
5725 case GDK_KP_Enter:
5726 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5727 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5728 sp_text_toolbox_size_changed (cbox, tbl);
5729 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5730 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5731 return TRUE; // I consumed the event
5732 break;
5733 }
5734 return FALSE;
5735 }
5737 void
5738 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5739 GObject *tbl)
5740 {
5741 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5742 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5743 int x, y;
5745 if (!popdown_visible)
5746 {
5747 gdk_window_get_origin (widget->window, &x, &y);
5748 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5749 gtk_widget_show_all (popdown);
5750 //sp_transientize (popdown);
5752 gdk_pointer_grab (widget->window, TRUE,
5753 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5754 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5755 GDK_POINTER_MOTION_MASK),
5756 NULL, NULL, GDK_CURRENT_TIME);
5758 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5760 popdown_visible = true;
5761 }
5762 else
5763 {
5764 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5765 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5766 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5767 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5768 gtk_widget_hide (popdown);
5769 popdown_visible = false;
5770 }
5771 }
5773 gboolean
5774 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5775 GdkEventFocus */*event*/,
5776 GObject */*tbl*/)
5777 {
5778 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5779 return FALSE;
5780 }
5782 gboolean
5783 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5784 GdkEventFocus */*event*/,
5785 GObject */*tbl*/)
5786 {
5787 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5789 if (popdown_hasfocus) {
5790 gtk_widget_hide (popdown);
5791 popdown_hasfocus = false;
5792 popdown_visible = false;
5793 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5794 return TRUE;
5795 }
5796 return FALSE;
5797 }
5799 gboolean
5800 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5801 GdkEventFocus */*event*/,
5802 GObject */*tbl*/)
5803 {
5804 popdown_hasfocus = true;
5805 return TRUE;
5806 }
5809 void
5810 cell_data_func (GtkTreeViewColumn */*column*/,
5811 GtkCellRenderer *cell,
5812 GtkTreeModel *tree_model,
5813 GtkTreeIter *iter,
5814 gpointer /*data*/)
5815 {
5816 gchar *family;
5817 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5818 gchar *const family_escaped = g_markup_escape_text(family, -1);
5820 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5821 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5823 std::stringstream markup;
5824 markup << family_escaped << " <span foreground='darkgray' font_family='"
5825 << family_escaped << "'>" << sample_escaped << "</span>";
5826 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5828 g_free(family);
5829 g_free(family_escaped);
5830 g_free(sample_escaped);
5831 }
5833 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5834 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5835 if (completion) {
5836 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5837 g_object_unref (completion);
5838 }
5839 }
5841 GtkWidget*
5842 sp_text_toolbox_new (SPDesktop *desktop)
5843 {
5844 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5845 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5847 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5848 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5850 GtkTooltips *tt = gtk_tooltips_new();
5851 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5853 ////////////Family
5854 //Window
5855 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5856 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5858 //Entry
5859 GtkWidget *entry = gtk_entry_new ();
5860 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5861 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5862 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5863 gtk_entry_completion_set_text_column (completion, 0);
5864 gtk_entry_completion_set_minimum_key_length (completion, 1);
5865 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5866 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5867 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5868 gtk_toolbar_append_widget( tbl, entry, "", "" );
5869 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5871 //Button
5872 GtkWidget *button = gtk_button_new ();
5873 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5874 gtk_toolbar_append_widget( tbl, button, "", "");
5876 //Popdown
5877 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5878 GtkWidget *treeview = gtk_tree_view_new ();
5880 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5881 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5882 gtk_tree_view_column_pack_start (column, cell, FALSE);
5883 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5884 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5885 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5887 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5888 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5889 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5891 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5893 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5894 gtk_container_add (GTK_CONTAINER (sw), treeview);
5896 gtk_container_add (GTK_CONTAINER (window), sw);
5897 gtk_widget_set_size_request (window, 300, 450);
5899 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5900 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5901 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5903 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5905 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5906 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5907 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5909 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5910 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5912 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5913 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5914 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5915 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5916 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5918 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5919 GtkWidget *box = gtk_event_box_new ();
5920 gtk_container_add (GTK_CONTAINER (box), image);
5921 gtk_toolbar_append_widget( tbl, box, "", "");
5922 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5923 GtkTooltips *tooltips = gtk_tooltips_new ();
5924 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5925 gtk_widget_hide (GTK_WIDGET (box));
5926 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5928 ////////////Size
5929 gchar const *const sizes[] = {
5930 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5931 "16", "18", "20", "22", "24", "28",
5932 "32", "36", "40", "48", "56", "64", "72", "144"
5933 };
5935 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5936 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5937 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5938 }
5939 gtk_widget_set_size_request (cbox, 80, -1);
5940 gtk_toolbar_append_widget( tbl, cbox, "", "");
5941 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5942 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5943 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5944 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5946 ////////////Text anchor
5947 GtkWidget *group = gtk_radio_button_new (NULL);
5948 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5949 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5951 // left
5952 GtkWidget *rbutton = group;
5953 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5954 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5955 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5957 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5958 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5959 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5960 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5962 // center
5963 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5964 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5965 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5966 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5968 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5969 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5970 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5971 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5973 // right
5974 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5975 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5976 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5977 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5979 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5980 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5981 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5982 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5984 // fill
5985 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5986 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5987 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5988 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5990 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5991 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5992 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5993 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5995 gtk_toolbar_append_widget( tbl, row, "", "");
5997 //spacer
5998 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6000 ////////////Text style
6001 row = gtk_hbox_new (FALSE, 4);
6003 // bold
6004 rbutton = gtk_toggle_button_new ();
6005 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6006 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6007 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6008 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6010 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6011 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6012 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6014 // italic
6015 rbutton = gtk_toggle_button_new ();
6016 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6017 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6018 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6019 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6021 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6022 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6023 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6025 gtk_toolbar_append_widget( tbl, row, "", "");
6027 //spacer
6028 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6030 ////////////Text orientation
6031 group = gtk_radio_button_new (NULL);
6032 row = gtk_hbox_new (FALSE, 4);
6033 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6035 // horizontal
6036 rbutton = group;
6037 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6038 gtk_container_add (GTK_CONTAINER (rbutton),
6039 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6040 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6041 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6043 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6044 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6045 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6047 // vertical
6048 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6049 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6050 gtk_container_add (GTK_CONTAINER (rbutton),
6051 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6052 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6053 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6055 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6056 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6057 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6058 gtk_toolbar_append_widget( tbl, row, "", "" );
6061 //watch selection
6062 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6064 sigc::connection *c_selection_changed =
6065 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6066 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6067 pool->add_connection ("selection-changed", c_selection_changed);
6069 sigc::connection *c_selection_modified =
6070 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6071 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6072 pool->add_connection ("selection-modified", c_selection_modified);
6074 sigc::connection *c_subselection_changed =
6075 new sigc::connection (desktop->connectToolSubselectionChanged
6076 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6077 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6079 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6082 gtk_widget_show_all( GTK_WIDGET(tbl) );
6084 return GTK_WIDGET(tbl);
6085 } // end of sp_text_toolbox_new()
6087 }//<unnamed> namespace
6090 //#########################
6091 //## Connector ##
6092 //#########################
6094 static void sp_connector_path_set_avoid(void)
6095 {
6096 cc_selection_set_avoid(true);
6097 }
6100 static void sp_connector_path_set_ignore(void)
6101 {
6102 cc_selection_set_avoid(false);
6103 }
6107 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6108 {
6109 // quit if run by the _changed callbacks
6110 if (g_object_get_data( tbl, "freeze" )) {
6111 return;
6112 }
6114 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6115 SPDocument *doc = sp_desktop_document(desktop);
6117 if (!sp_document_get_undo_sensitive(doc))
6118 {
6119 return;
6120 }
6122 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6124 if ( repr->attribute("inkscape:connector-spacing") ) {
6125 gdouble priorValue = gtk_adjustment_get_value(adj);
6126 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6127 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6128 return;
6129 }
6130 } else if ( adj->value == defaultConnSpacing ) {
6131 return;
6132 }
6134 // in turn, prevent callbacks from responding
6135 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6137 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6138 SP_OBJECT(desktop->namedview)->updateRepr();
6140 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6141 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6142 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6143 NR::Matrix m = NR::identity();
6144 avoid_item_move(&m, item);
6145 }
6147 if (items) {
6148 g_slist_free(items);
6149 }
6151 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6152 _("Change connector spacing"));
6154 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6156 spinbutton_defocus(GTK_OBJECT(tbl));
6157 }
6159 static void sp_connector_graph_layout(void)
6160 {
6161 if (!SP_ACTIVE_DESKTOP) return;
6163 // hack for clones, see comment in align-and-distribute.cpp
6164 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6165 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6167 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6169 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6171 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6172 }
6174 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6175 {
6176 if ( gtk_toggle_action_get_active( act ) ) {
6177 prefs_set_string_attribute("tools.connector", "directedlayout",
6178 "true");
6179 } else {
6180 prefs_set_string_attribute("tools.connector", "directedlayout",
6181 "false");
6182 }
6183 }
6185 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6186 {
6187 if ( gtk_toggle_action_get_active( act ) ) {
6188 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6189 "true");
6190 } else {
6191 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6192 "false");
6193 }
6194 }
6197 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6198 {
6199 prefs_set_double_attribute("tools.connector", "length", adj->value);
6200 spinbutton_defocus(GTK_OBJECT(tbl));
6201 }
6203 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6204 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6205 bool /*is_interactive*/, gpointer data)
6206 {
6207 GtkWidget *tbl = GTK_WIDGET(data);
6209 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6210 return;
6211 }
6212 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6213 return;
6214 }
6216 GtkAdjustment *adj = (GtkAdjustment*)
6217 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6218 gdouble spacing = defaultConnSpacing;
6219 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6221 gtk_adjustment_set_value(adj, spacing);
6222 }
6225 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6226 NULL, /* child_added */
6227 NULL, /* child_removed */
6228 connector_tb_event_attr_changed,
6229 NULL, /* content_changed */
6230 NULL /* order_changed */
6231 };
6234 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6235 {
6236 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6238 {
6239 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6240 _("Avoid"),
6241 _("Make connectors avoid selected objects"),
6242 "connector_avoid",
6243 secondarySize );
6244 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6245 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6246 }
6248 {
6249 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6250 _("Ignore"),
6251 _("Make connectors ignore selected objects"),
6252 "connector_ignore",
6253 secondarySize );
6254 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6255 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6256 }
6258 EgeAdjustmentAction* eact = 0;
6260 // Spacing spinbox
6261 eact = create_adjustment_action( "ConnectorSpacingAction",
6262 _("Connector Spacing"), _("Spacing:"),
6263 _("The amount of space left around objects by auto-routing connectors"),
6264 "tools.connector", "spacing", defaultConnSpacing,
6265 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6266 0, 100, 1.0, 10.0,
6267 0, 0, 0,
6268 connector_spacing_changed, 1, 0 );
6269 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6271 // Graph (connector network) layout
6272 {
6273 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6274 _("Graph"),
6275 _("Nicely arrange selected connector network"),
6276 "graph_layout",
6277 secondarySize );
6278 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6279 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6280 }
6282 // Default connector length spinbox
6283 eact = create_adjustment_action( "ConnectorLengthAction",
6284 _("Connector Length"), _("Length:"),
6285 _("Ideal length for connectors when layout is applied"),
6286 "tools.connector", "length", 100,
6287 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6288 10, 1000, 10.0, 100.0,
6289 0, 0, 0,
6290 connector_length_changed, 1, 0 );
6291 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6294 // Directed edges toggle button
6295 {
6296 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6297 _("Downwards"),
6298 _("Make connectors with end-markers (arrows) point downwards"),
6299 "directed_graph",
6300 Inkscape::ICON_SIZE_DECORATION );
6301 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6303 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6304 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6305 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6307 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6308 }
6310 // Avoid overlaps toggle button
6311 {
6312 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6313 _("Remove overlaps"),
6314 _("Do not allow overlapping shapes"),
6315 "remove_overlaps",
6316 Inkscape::ICON_SIZE_DECORATION );
6317 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6319 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6320 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6321 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6323 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6324 }
6326 // Code to watch for changes to the connector-spacing attribute in
6327 // the XML.
6328 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6329 g_assert(repr != NULL);
6331 purge_repr_listener( holder, holder );
6333 if (repr) {
6334 g_object_set_data( holder, "repr", repr );
6335 Inkscape::GC::anchor(repr);
6336 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6337 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6338 }
6339 } // end of sp_connector_toolbox_prep()
6342 //#########################
6343 //## Paintbucket ##
6344 //#########################
6346 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6347 {
6348 gint channels = ege_select_one_action_get_active( act );
6349 flood_channels_set_channels( channels );
6350 }
6352 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6353 {
6354 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6355 }
6357 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6358 {
6359 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6360 }
6362 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6363 {
6364 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6365 SPUnit const *unit = tracker->getActiveUnit();
6367 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6369 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6370 }
6372 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6373 {
6374 // FIXME: make defaults settable via Inkscape Options
6375 struct KeyValue {
6376 char const *key;
6377 double value;
6378 } const key_values[] = {
6379 {"threshold", 15},
6380 {"offset", 0.0}
6381 };
6383 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6384 KeyValue const &kv = key_values[i];
6385 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6386 if ( adj ) {
6387 gtk_adjustment_set_value(adj, kv.value);
6388 }
6389 }
6391 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6392 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6393 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6394 ege_select_one_action_set_active( autogap_action, 0 );
6395 }
6397 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6398 {
6399 EgeAdjustmentAction* eact = 0;
6401 {
6402 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6404 GList* items = 0;
6405 gint count = 0;
6406 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6407 {
6408 GtkTreeIter iter;
6409 gtk_list_store_append( model, &iter );
6410 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6411 count++;
6412 }
6413 g_list_free( items );
6414 items = 0;
6415 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6416 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6417 ege_select_one_action_set_appearance( act1, "compact" );
6418 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6419 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6420 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6421 g_object_set_data( holder, "channels_action", act1 );
6422 }
6424 // Spacing spinbox
6425 {
6426 eact = create_adjustment_action(
6427 "ThresholdAction",
6428 _("Fill Threshold"), _("Threshold:"),
6429 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6430 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6431 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6432 0, 0, 0,
6433 paintbucket_threshold_changed, 1, 0 );
6435 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6436 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6437 }
6439 // Create the units menu.
6440 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6441 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6442 if (stored_unit)
6443 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6444 g_object_set_data( holder, "tracker", tracker );
6445 {
6446 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6447 gtk_action_group_add_action( mainActions, act );
6448 }
6450 // Offset spinbox
6451 {
6452 eact = create_adjustment_action(
6453 "OffsetAction",
6454 _("Grow/shrink by"), _("Grow/shrink by:"),
6455 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6456 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6457 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6458 0, 0, 0,
6459 paintbucket_offset_changed, 1, 2);
6460 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6462 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6463 }
6465 /* Auto Gap */
6466 {
6467 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6469 GList* items = 0;
6470 gint count = 0;
6471 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6472 {
6473 GtkTreeIter iter;
6474 gtk_list_store_append( model, &iter );
6475 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6476 count++;
6477 }
6478 g_list_free( items );
6479 items = 0;
6480 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6481 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6482 ege_select_one_action_set_appearance( act2, "compact" );
6483 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6484 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6485 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6486 g_object_set_data( holder, "autogap_action", act2 );
6487 }
6489 /* Reset */
6490 {
6491 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6492 _("Defaults"),
6493 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6494 GTK_STOCK_CLEAR );
6495 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6496 gtk_action_group_add_action( mainActions, act );
6497 gtk_action_set_sensitive( act, TRUE );
6498 }
6500 }
6502 /*
6503 Local Variables:
6504 mode:c++
6505 c-file-style:"stroustrup"
6506 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6507 indent-tabs-mode:nil
6508 fill-column:99
6509 End:
6510 */
6511 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :