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 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include "xml/attribute-record.h"
55 #include <glibmm/i18n.h>
56 #include "helper/unit-menu.h"
57 #include "helper/units.h"
58 #include "live_effects/effect.h"
60 #include "inkscape.h"
61 #include "conn-avoid-ref.h"
64 #include "select-toolbar.h"
65 #include "gradient-toolbar.h"
67 #include "connector-context.h"
68 #include "node-context.h"
69 #include "draw-context.h"
70 #include "shape-editor.h"
71 #include "tweak-context.h"
72 #include "sp-rect.h"
73 #include "box3d.h"
74 #include "box3d-context.h"
75 #include "sp-star.h"
76 #include "sp-spiral.h"
77 #include "sp-ellipse.h"
78 #include "sp-text.h"
79 #include "sp-flowtext.h"
80 #include "sp-clippath.h"
81 #include "sp-mask.h"
82 #include "style.h"
83 #include "selection.h"
84 #include "selection-chemistry.h"
85 #include "document-private.h"
86 #include "desktop-style.h"
87 #include "../libnrtype/font-lister.h"
88 #include "../libnrtype/font-instance.h"
89 #include "../connection-pool.h"
90 #include "../prefs-utils.h"
91 #include "../inkscape-stock.h"
92 #include "icon.h"
93 #include "graphlayout/graphlayout.h"
94 #include "interface.h"
95 #include "shortcuts.h"
97 #include "mod360.h"
99 #include "toolbox.h"
101 #include "flood-context.h"
103 #include "ink-action.h"
104 #include "ege-adjustment-action.h"
105 #include "ege-output-action.h"
106 #include "ege-select-one-action.h"
107 #include "helper/unit-tracker.h"
109 #include "svg/css-ostringstream.h"
111 #include "widgets/calligraphic-profile-rename.h"
113 using Inkscape::UnitTracker;
115 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
116 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
118 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
131 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
138 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
139 static Inkscape::IconSize sizeChoices[] = {
140 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
141 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
142 Inkscape::ICON_SIZE_MENU
143 };
144 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
145 return sizeChoices[index];
146 }
148 static struct {
149 gchar const *type_name;
150 gchar const *data_name;
151 sp_verb_t verb;
152 sp_verb_t doubleclick_verb;
153 } const tools[] = {
154 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
155 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
156 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
157 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
158 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
159 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
160 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
161 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
162 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
163 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
164 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
165 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
166 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
167 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
168 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
169 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
170 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
171 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
172 { NULL, NULL, 0, 0 }
173 };
175 static struct {
176 gchar const *type_name;
177 gchar const *data_name;
178 GtkWidget *(*create_func)(SPDesktop *desktop);
179 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
180 gchar const *ui_name;
181 gint swatch_verb_id;
182 gchar const *swatch_tool;
183 gchar const *swatch_tip;
184 } const aux_toolboxes[] = {
185 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
186 SP_VERB_INVALID, 0, 0},
187 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
188 SP_VERB_INVALID, 0, 0},
189 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
190 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
191 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
192 SP_VERB_INVALID, 0, 0},
193 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
194 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
195 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
196 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
197 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
198 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
199 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
200 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
201 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
202 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
203 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
204 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
205 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
206 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
207 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
208 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
209 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
210 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
211 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
212 SP_VERB_INVALID, 0, 0},
213 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
214 SP_VERB_INVALID, 0, 0},
215 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
216 SP_VERB_INVALID, 0, 0},
217 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
218 SP_VERB_INVALID, 0, 0},
219 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
220 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
221 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
222 };
224 #define TOOLBAR_SLIDER_HINT "full"
226 static gchar const * ui_descr =
227 "<ui>"
228 " <toolbar name='SelectToolbar'>"
229 " <toolitem action='EditSelectAll' />"
230 " <toolitem action='EditSelectAllInAllLayers' />"
231 " <toolitem action='EditDeselect' />"
232 " <separator />"
233 " <toolitem action='ObjectRotate90CCW' />"
234 " <toolitem action='ObjectRotate90' />"
235 " <toolitem action='ObjectFlipHorizontally' />"
236 " <toolitem action='ObjectFlipVertically' />"
237 " <separator />"
238 " <toolitem action='SelectionToBack' />"
239 " <toolitem action='SelectionLower' />"
240 " <toolitem action='SelectionRaise' />"
241 " <toolitem action='SelectionToFront' />"
242 " <separator />"
243 " <toolitem action='XAction' />"
244 " <toolitem action='YAction' />"
245 " <toolitem action='WidthAction' />"
246 " <toolitem action='LockAction' />"
247 " <toolitem action='HeightAction' />"
248 " <toolitem action='UnitsAction' />"
249 " <separator />"
250 " <toolitem action='transform_affect_label' />"
251 " <toolitem action='transform_stroke' />"
252 " <toolitem action='transform_corners' />"
253 " <toolitem action='transform_gradient' />"
254 " <toolitem action='transform_pattern' />"
255 " </toolbar>"
257 " <toolbar name='NodeToolbar'>"
258 " <toolitem action='NodeInsertAction' />"
259 " <toolitem action='NodeDeleteAction' />"
260 " <separator />"
261 " <toolitem action='NodeJoinAction' />"
262 " <toolitem action='NodeBreakAction' />"
263 " <separator />"
264 " <toolitem action='NodeJoinSegmentAction' />"
265 " <toolitem action='NodeDeleteSegmentAction' />"
266 " <separator />"
267 " <toolitem action='NodeCuspAction' />"
268 " <toolitem action='NodeSmoothAction' />"
269 " <toolitem action='NodeSymmetricAction' />"
270 " <separator />"
271 " <toolitem action='NodeLineAction' />"
272 " <toolitem action='NodeCurveAction' />"
273 " <separator />"
274 " <toolitem action='ObjectToPath' />"
275 " <toolitem action='StrokeToPath' />"
276 " <separator />"
277 " <toolitem action='NodeXAction' />"
278 " <toolitem action='NodeYAction' />"
279 " <toolitem action='NodeUnitsAction' />"
280 " <separator />"
281 " <toolitem action='ObjectEditClipPathAction' />"
282 " <toolitem action='ObjectEditMaskPathAction' />"
283 " <toolitem action='EditNextLPEParameterAction' />"
284 " <separator />"
285 " <toolitem action='NodesShowHandlesAction' />"
286 " <toolitem action='NodesShowHelperpath' />"
287 " </toolbar>"
289 " <toolbar name='TweakToolbar'>"
290 " <toolitem action='TweakWidthAction' />"
291 " <separator />"
292 " <toolitem action='TweakForceAction' />"
293 " <toolitem action='TweakPressureAction' />"
294 " <separator />"
295 " <toolitem action='TweakModeAction' />"
296 " <separator />"
297 " <toolitem action='TweakFidelityAction' />"
298 " <separator />"
299 " <toolitem action='TweakChannelsLabel' />"
300 " <toolitem action='TweakDoH' />"
301 " <toolitem action='TweakDoS' />"
302 " <toolitem action='TweakDoL' />"
303 " <toolitem action='TweakDoO' />"
304 " </toolbar>"
306 " <toolbar name='ZoomToolbar'>"
307 " <toolitem action='ZoomIn' />"
308 " <toolitem action='ZoomOut' />"
309 " <separator />"
310 " <toolitem action='Zoom1:0' />"
311 " <toolitem action='Zoom1:2' />"
312 " <toolitem action='Zoom2:1' />"
313 " <separator />"
314 " <toolitem action='ZoomSelection' />"
315 " <toolitem action='ZoomDrawing' />"
316 " <toolitem action='ZoomPage' />"
317 " <toolitem action='ZoomPageWidth' />"
318 " <separator />"
319 " <toolitem action='ZoomPrev' />"
320 " <toolitem action='ZoomNext' />"
321 " </toolbar>"
323 " <toolbar name='StarToolbar'>"
324 " <separator />"
325 " <toolitem action='StarStateAction' />"
326 " <separator />"
327 " <toolitem action='FlatAction' />"
328 " <separator />"
329 " <toolitem action='MagnitudeAction' />"
330 " <toolitem action='SpokeAction' />"
331 " <toolitem action='RoundednessAction' />"
332 " <toolitem action='RandomizationAction' />"
333 " <separator />"
334 " <toolitem action='StarResetAction' />"
335 " </toolbar>"
337 " <toolbar name='RectToolbar'>"
338 " <toolitem action='RectStateAction' />"
339 " <toolitem action='RectWidthAction' />"
340 " <toolitem action='RectHeightAction' />"
341 " <toolitem action='RadiusXAction' />"
342 " <toolitem action='RadiusYAction' />"
343 " <toolitem action='RectUnitsAction' />"
344 " <separator />"
345 " <toolitem action='RectResetAction' />"
346 " </toolbar>"
348 " <toolbar name='3DBoxToolbar'>"
349 " <toolitem action='3DBoxAngleXAction' />"
350 " <toolitem action='3DBoxVPXStateAction' />"
351 " <separator />"
352 " <toolitem action='3DBoxAngleYAction' />"
353 " <toolitem action='3DBoxVPYStateAction' />"
354 " <separator />"
355 " <toolitem action='3DBoxAngleZAction' />"
356 " <toolitem action='3DBoxVPZStateAction' />"
357 " </toolbar>"
359 " <toolbar name='SpiralToolbar'>"
360 " <toolitem action='SpiralStateAction' />"
361 " <toolitem action='SpiralRevolutionAction' />"
362 " <toolitem action='SpiralExpansionAction' />"
363 " <toolitem action='SpiralT0Action' />"
364 " <separator />"
365 " <toolitem action='SpiralResetAction' />"
366 " </toolbar>"
368 " <toolbar name='PenToolbar'>"
369 " <toolitem action='FreehandModeActionPenTemp' />"
370 " <toolitem action='FreehandModeActionPen' />"
371 " <separator />"
372 " <toolitem action='SetPenShapeAction'/>"
373 " </toolbar>"
375 " <toolbar name='PencilToolbar'>"
376 " <toolitem action='FreehandModeActionPencilTemp' />"
377 " <toolitem action='FreehandModeActionPencil' />"
378 " <separator />"
379 " <toolitem action='PencilToleranceAction' />"
380 " <separator />"
381 " <toolitem action='PencilResetAction' />"
382 " <separator />"
383 " <toolitem action='SetPencilShapeAction'/>"
384 " </toolbar>"
386 " <toolbar name='CalligraphyToolbar'>"
387 " <separator />"
388 " <toolitem action='SetProfileAction'/>"
389 " <toolitem action='SaveDeleteProfileAction'/>"
390 " <separator />"
391 " <toolitem action='CalligraphyWidthAction' />"
392 " <toolitem action='PressureAction' />"
393 " <toolitem action='TraceAction' />"
394 " <toolitem action='ThinningAction' />"
395 " <separator />"
396 " <toolitem action='AngleAction' />"
397 " <toolitem action='TiltAction' />"
398 " <toolitem action='FixationAction' />"
399 " <separator />"
400 " <toolitem action='CapRoundingAction' />"
401 " <separator />"
402 " <toolitem action='TremorAction' />"
403 " <toolitem action='WiggleAction' />"
404 " <toolitem action='MassAction' />"
405 " <separator />"
406 " </toolbar>"
408 " <toolbar name='ArcToolbar'>"
409 " <toolitem action='ArcStateAction' />"
410 " <separator />"
411 " <toolitem action='ArcStartAction' />"
412 " <toolitem action='ArcEndAction' />"
413 " <separator />"
414 " <toolitem action='ArcOpenAction' />"
415 " <separator />"
416 " <toolitem action='ArcResetAction' />"
417 " <separator />"
418 " </toolbar>"
420 " <toolbar name='PaintbucketToolbar'>"
421 " <toolitem action='ChannelsAction' />"
422 " <separator />"
423 " <toolitem action='ThresholdAction' />"
424 " <separator />"
425 " <toolitem action='OffsetAction' />"
426 " <toolitem action='PaintbucketUnitsAction' />"
427 " <separator />"
428 " <toolitem action='AutoGapAction' />"
429 " <separator />"
430 " <toolitem action='PaintbucketResetAction' />"
431 " </toolbar>"
433 " <toolbar name='EraserToolbar'>"
434 " <toolitem action='EraserWidthAction' />"
435 " <separator />"
436 " <toolitem action='EraserModeAction' />"
437 " </toolbar>"
439 " <toolbar name='DropperToolbar'>"
440 " <toolitem action='DropperOpacityAction' />"
441 " <toolitem action='DropperPickAlphaAction' />"
442 " <toolitem action='DropperSetAlphaAction' />"
443 " </toolbar>"
445 " <toolbar name='ConnectorToolbar'>"
446 " <toolitem action='ConnectorAvoidAction' />"
447 " <toolitem action='ConnectorIgnoreAction' />"
448 " <toolitem action='ConnectorSpacingAction' />"
449 " <toolitem action='ConnectorGraphAction' />"
450 " <toolitem action='ConnectorLengthAction' />"
451 " <toolitem action='ConnectorDirectedAction' />"
452 " <toolitem action='ConnectorOverlapAction' />"
453 " </toolbar>"
455 "</ui>"
456 ;
458 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
460 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
462 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
463 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
465 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
466 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
468 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
469 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
472 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
473 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
474 Inkscape::UI::View::View *view, GtkTooltips *tt);
476 class VerbAction : public Gtk::Action {
477 public:
478 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
480 virtual ~VerbAction();
481 virtual void set_active(bool active = true);
483 protected:
484 virtual Gtk::Widget* create_menu_item_vfunc();
485 virtual Gtk::Widget* create_tool_item_vfunc();
487 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
488 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
490 virtual void on_activate();
492 private:
493 Inkscape::Verb* verb;
494 Inkscape::Verb* verb2;
495 Inkscape::UI::View::View *view;
496 GtkTooltips *tooltips;
497 bool active;
499 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
500 };
503 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
504 {
505 Glib::RefPtr<VerbAction> result;
506 SPAction *action = verb->get_action(view);
507 if ( action ) {
508 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
509 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
510 }
512 return result;
513 }
515 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
516 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
517 verb(verb),
518 verb2(verb2),
519 view(view),
520 tooltips(tooltips),
521 active(false)
522 {
523 }
525 VerbAction::~VerbAction()
526 {
527 }
529 Gtk::Widget* VerbAction::create_menu_item_vfunc()
530 {
531 // First call in to get the icon rendered if present in SVG
532 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
533 delete widget;
534 widget = 0;
536 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
537 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
538 return widg;
539 }
541 Gtk::Widget* VerbAction::create_tool_item_vfunc()
542 {
543 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
544 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
545 GtkWidget* toolbox = 0;
546 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
547 SP_BUTTON_TYPE_TOGGLE,
548 verb,
549 verb2,
550 view,
551 tooltips );
552 if ( active ) {
553 sp_button_toggle_set_down( SP_BUTTON(button), active);
554 }
555 gtk_widget_show_all( button );
556 Gtk::Widget* wrapped = Glib::wrap(button);
557 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
558 holder->add(*wrapped);
560 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
561 return holder;
562 }
564 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
565 {
566 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
567 Gtk::Action::connect_proxy_vfunc(proxy);
568 }
570 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
571 {
572 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
573 Gtk::Action::disconnect_proxy_vfunc(proxy);
574 }
576 void VerbAction::set_active(bool active)
577 {
578 this->active = active;
579 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
580 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
581 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
582 if (ti) {
583 // *should* have one child that is the SPButton
584 Gtk::Widget* child = ti->get_child();
585 if ( child && SP_IS_BUTTON(child->gobj()) ) {
586 SPButton* button = SP_BUTTON(child->gobj());
587 sp_button_toggle_set_down( button, active );
588 }
589 }
590 }
591 }
593 void VerbAction::on_activate()
594 {
595 if ( verb ) {
596 SPAction *action = verb->get_action(view);
597 if ( action ) {
598 sp_action_perform(action, 0);
599 }
600 }
601 }
603 /* Global text entry widgets necessary for update */
604 /* GtkWidget *dropper_rgb_entry,
605 *dropper_opacity_entry ; */
606 // should be made a private member once this is converted to class
608 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
609 connection->disconnect();
610 delete connection;
611 }
613 static void purge_repr_listener( GObject* obj, GObject* tbl )
614 {
615 (void)obj;
616 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
617 if (oldrepr) { // remove old listener
618 sp_repr_remove_listener_by_data(oldrepr, tbl);
619 Inkscape::GC::release(oldrepr);
620 oldrepr = 0;
621 g_object_set_data( tbl, "repr", NULL );
622 }
623 }
625 GtkWidget *
626 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
627 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
628 Inkscape::UI::View::View *view, GtkTooltips *tt)
629 {
630 SPAction *action = verb->get_action(view);
631 if (!action) return NULL;
633 SPAction *doubleclick_action;
634 if (doubleclick_verb)
635 doubleclick_action = doubleclick_verb->get_action(view);
636 else
637 doubleclick_action = NULL;
639 /* fixme: Handle sensitive/unsensitive */
640 /* fixme: Implement sp_button_new_from_action */
641 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
642 gtk_widget_show(b);
645 unsigned int shortcut = sp_shortcut_get_primary(verb);
646 if (shortcut) {
647 gchar key[256];
648 sp_ui_shortcut_string(shortcut, key);
649 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
650 if ( t ) {
651 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
652 }
653 g_free(tip);
654 } else {
655 if ( t ) {
656 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
657 }
658 }
660 return b;
661 }
664 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
665 {
666 SPAction* targetAction = SP_ACTION(user_data);
667 if ( targetAction ) {
668 sp_action_perform( targetAction, NULL );
669 }
670 }
672 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
673 {
674 if ( data ) {
675 GtkAction* act = GTK_ACTION(data);
676 gtk_action_set_sensitive( act, sensitive );
677 }
678 }
680 static SPActionEventVector action_event_vector = {
681 {NULL},
682 NULL,
683 NULL,
684 sp_action_action_set_sensitive,
685 NULL,
686 NULL
687 };
689 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
690 {
691 GtkAction* act = 0;
693 SPAction* targetAction = verb->get_action(view);
694 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
695 act = GTK_ACTION(inky);
696 gtk_action_set_sensitive( act, targetAction->sensitive );
698 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
700 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
701 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
703 return act;
704 }
706 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
707 {
708 Inkscape::UI::View::View *view = desktop;
709 gint verbsToUse[] = {
710 // disabled until we have icons for them:
711 //find
712 //SP_VERB_EDIT_TILE,
713 //SP_VERB_EDIT_UNTILE,
714 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
715 SP_VERB_DIALOG_DISPLAY,
716 SP_VERB_DIALOG_FILL_STROKE,
717 SP_VERB_DIALOG_NAMEDVIEW,
718 SP_VERB_DIALOG_TEXT,
719 SP_VERB_DIALOG_XML_EDITOR,
720 SP_VERB_EDIT_CLONE,
721 SP_VERB_EDIT_COPY,
722 SP_VERB_EDIT_CUT,
723 SP_VERB_EDIT_DUPLICATE,
724 SP_VERB_EDIT_PASTE,
725 SP_VERB_EDIT_REDO,
726 SP_VERB_EDIT_UNDO,
727 SP_VERB_EDIT_UNLINK_CLONE,
728 SP_VERB_FILE_EXPORT,
729 SP_VERB_FILE_IMPORT,
730 SP_VERB_FILE_NEW,
731 SP_VERB_FILE_OPEN,
732 SP_VERB_FILE_PRINT,
733 SP_VERB_FILE_SAVE,
734 SP_VERB_OBJECT_TO_CURVE,
735 SP_VERB_SELECTION_GROUP,
736 SP_VERB_SELECTION_OUTLINE,
737 SP_VERB_SELECTION_UNGROUP,
738 SP_VERB_ZOOM_1_1,
739 SP_VERB_ZOOM_1_2,
740 SP_VERB_ZOOM_2_1,
741 SP_VERB_ZOOM_DRAWING,
742 SP_VERB_ZOOM_IN,
743 SP_VERB_ZOOM_NEXT,
744 SP_VERB_ZOOM_OUT,
745 SP_VERB_ZOOM_PAGE,
746 SP_VERB_ZOOM_PAGE_WIDTH,
747 SP_VERB_ZOOM_PREV,
748 SP_VERB_ZOOM_SELECTION,
749 };
751 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
753 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
754 Glib::RefPtr<Gtk::ActionGroup> mainActions;
755 if ( groups.find(desktop) != groups.end() ) {
756 mainActions = groups[desktop];
757 }
759 if ( !mainActions ) {
760 mainActions = Gtk::ActionGroup::create("main");
761 groups[desktop] = mainActions;
762 }
764 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
765 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
766 if ( verb ) {
767 if (!mainActions->get_action(verb->get_id())) {
768 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
769 mainActions->add(Glib::wrap(act));
770 }
771 }
772 }
774 if ( !mainActions->get_action("ToolZoom") ) {
775 GtkTooltips *tt = gtk_tooltips_new();
776 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
777 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
778 if ( va ) {
779 mainActions->add(va);
780 if ( i == 0 ) {
781 va->set_active(true);
782 }
783 }
784 }
785 }
788 return mainActions;
789 }
792 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
793 {
794 gtk_widget_set_size_request( widget,
795 widget->allocation.width,
796 widget->allocation.height );
797 }
799 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
800 {
801 gtk_widget_set_size_request( widget, -1, -1 );
802 }
806 GtkWidget *
807 sp_tool_toolbox_new()
808 {
809 GtkTooltips *tt = gtk_tooltips_new();
810 GtkWidget* tb = gtk_toolbar_new();
811 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
812 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
814 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
815 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
817 gtk_widget_set_sensitive(tb, FALSE);
819 GtkWidget *hb = gtk_handle_box_new();
820 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
821 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
822 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
824 gtk_container_add(GTK_CONTAINER(hb), tb);
825 gtk_widget_show(GTK_WIDGET(tb));
827 sigc::connection* conn = new sigc::connection;
828 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
830 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
831 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
833 return hb;
834 }
836 GtkWidget *
837 sp_aux_toolbox_new()
838 {
839 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
841 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
843 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
845 gtk_widget_set_sensitive(tb, FALSE);
847 GtkWidget *hb = gtk_handle_box_new();
848 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
849 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
850 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
852 gtk_container_add(GTK_CONTAINER(hb), tb);
853 gtk_widget_show(GTK_WIDGET(tb));
855 sigc::connection* conn = new sigc::connection;
856 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
858 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
859 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
861 return hb;
862 }
864 //####################################
865 //# Commands Bar
866 //####################################
868 GtkWidget *
869 sp_commands_toolbox_new()
870 {
871 GtkWidget *tb = gtk_toolbar_new();
873 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
874 gtk_widget_set_sensitive(tb, FALSE);
876 GtkWidget *hb = gtk_handle_box_new();
877 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
878 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
879 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
881 gtk_container_add(GTK_CONTAINER(hb), tb);
882 gtk_widget_show(GTK_WIDGET(tb));
884 sigc::connection* conn = new sigc::connection;
885 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
887 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
888 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
890 return hb;
891 }
894 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
895 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
896 gchar const *path, gchar const *data, gdouble def,
897 GtkWidget *focusTarget,
898 GtkWidget *us,
899 GObject *dataKludge,
900 gboolean altx, gchar const *altx_mark,
901 gdouble lower, gdouble upper, gdouble step, gdouble page,
902 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
903 void (*callback)(GtkAdjustment *, GObject *),
904 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
905 {
906 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
907 lower, upper, step, page, page ) );
908 if (us) {
909 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
910 }
912 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
914 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
915 if ( shortLabel ) {
916 g_object_set( act, "short_label", shortLabel, NULL );
917 }
919 if ( (descrCount > 0) && descrLabels && descrValues ) {
920 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
921 }
923 if ( focusTarget ) {
924 ege_adjustment_action_set_focuswidget( act, focusTarget );
925 }
927 if ( altx && altx_mark ) {
928 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
929 }
931 if ( dataKludge ) {
932 g_object_set_data( dataKludge, data, adj );
933 }
935 // Using a cast just to make sure we pass in the right kind of function pointer
936 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
938 return act;
939 }
942 //####################################
943 //# node editing callbacks
944 //####################################
946 /**
947 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
948 */
949 static ShapeEditor *get_current_shape_editor()
950 {
951 if (!SP_ACTIVE_DESKTOP) {
952 return NULL;
953 }
955 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
957 if (!SP_IS_NODE_CONTEXT(event_context)) {
958 return NULL;
959 }
961 return SP_NODE_CONTEXT(event_context)->shape_editor;
962 }
965 void
966 sp_node_path_edit_add(void)
967 {
968 ShapeEditor *shape_editor = get_current_shape_editor();
969 if (shape_editor) shape_editor->add_node();
970 }
972 void
973 sp_node_path_edit_delete(void)
974 {
975 ShapeEditor *shape_editor = get_current_shape_editor();
976 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
977 }
979 void
980 sp_node_path_edit_delete_segment(void)
981 {
982 ShapeEditor *shape_editor = get_current_shape_editor();
983 if (shape_editor) shape_editor->delete_segment();
984 }
986 void
987 sp_node_path_edit_break(void)
988 {
989 ShapeEditor *shape_editor = get_current_shape_editor();
990 if (shape_editor) shape_editor->break_at_nodes();
991 }
993 void
994 sp_node_path_edit_join(void)
995 {
996 ShapeEditor *shape_editor = get_current_shape_editor();
997 if (shape_editor) shape_editor->join_nodes();
998 }
1000 void
1001 sp_node_path_edit_join_segment(void)
1002 {
1003 ShapeEditor *shape_editor = get_current_shape_editor();
1004 if (shape_editor) shape_editor->join_segments();
1005 }
1007 void
1008 sp_node_path_edit_toline(void)
1009 {
1010 ShapeEditor *shape_editor = get_current_shape_editor();
1011 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1012 }
1014 void
1015 sp_node_path_edit_tocurve(void)
1016 {
1017 ShapeEditor *shape_editor = get_current_shape_editor();
1018 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1019 }
1021 void
1022 sp_node_path_edit_cusp(void)
1023 {
1024 ShapeEditor *shape_editor = get_current_shape_editor();
1025 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1026 }
1028 void
1029 sp_node_path_edit_smooth(void)
1030 {
1031 ShapeEditor *shape_editor = get_current_shape_editor();
1032 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1033 }
1035 void
1036 sp_node_path_edit_symmetrical(void)
1037 {
1038 ShapeEditor *shape_editor = get_current_shape_editor();
1039 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1040 }
1042 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1043 bool show = gtk_toggle_action_get_active( act );
1044 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1045 ShapeEditor *shape_editor = get_current_shape_editor();
1046 if (shape_editor) shape_editor->show_handles(show);
1047 }
1049 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1050 bool show = gtk_toggle_action_get_active( act );
1051 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1052 ShapeEditor *shape_editor = get_current_shape_editor();
1053 if (shape_editor) shape_editor->show_helperpath(show);
1054 }
1056 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1057 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1058 }
1060 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1061 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1062 }
1064 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1065 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1066 }
1068 /* is called when the node selection is modified */
1069 static void
1070 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1071 {
1072 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1073 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1074 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1075 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1077 // quit if run by the attr_changed listener
1078 if (g_object_get_data( tbl, "freeze" )) {
1079 return;
1080 }
1082 // in turn, prevent listener from responding
1083 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1085 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1086 SPUnit const *unit = tracker->getActiveUnit();
1088 ShapeEditor *shape_editor = get_current_shape_editor();
1089 if (shape_editor && shape_editor->has_nodepath()) {
1090 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1091 int n_selected = 0;
1092 if (nodepath) {
1093 n_selected = nodepath->numSelected();
1094 }
1096 if (n_selected == 0) {
1097 gtk_action_set_sensitive(xact, FALSE);
1098 gtk_action_set_sensitive(yact, FALSE);
1099 } else {
1100 gtk_action_set_sensitive(xact, TRUE);
1101 gtk_action_set_sensitive(yact, TRUE);
1102 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1103 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1105 if (n_selected == 1) {
1106 NR::Point sel_node = nodepath->singleSelectedCoords();
1107 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1108 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1109 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1110 }
1111 } else {
1112 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1113 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1114 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1115 /* Note: Currently x and y will always have a value, even if the coordinates of the
1116 selected nodes don't coincide (in this case we use the coordinates of the center
1117 of the bounding box). So the entries are never set to zero. */
1118 // FIXME: Maybe we should clear the entry if several nodes are selected
1119 // instead of providing a kind of average value
1120 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1121 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1122 }
1123 }
1124 }
1125 } else {
1126 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1127 gtk_action_set_sensitive(xact, FALSE);
1128 gtk_action_set_sensitive(yact, FALSE);
1129 }
1131 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1132 }
1134 static void
1135 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1136 {
1137 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1139 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1140 SPUnit const *unit = tracker->getActiveUnit();
1142 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1143 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1144 }
1146 // quit if run by the attr_changed listener
1147 if (g_object_get_data( tbl, "freeze" )) {
1148 return;
1149 }
1151 // in turn, prevent listener from responding
1152 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1154 ShapeEditor *shape_editor = get_current_shape_editor();
1155 if (shape_editor && shape_editor->has_nodepath()) {
1156 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1157 if (!strcmp(value_name, "x")) {
1158 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1159 }
1160 if (!strcmp(value_name, "y")) {
1161 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1162 }
1163 }
1165 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1166 }
1168 static void
1169 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1170 {
1171 sp_node_path_value_changed(adj, tbl, "x");
1172 }
1174 static void
1175 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1176 {
1177 sp_node_path_value_changed(adj, tbl, "y");
1178 }
1180 void
1181 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1182 {
1183 {
1184 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1185 SPItem *item = selection->singleItem();
1186 if (item && SP_IS_LPE_ITEM(item)) {
1187 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1188 gtk_action_set_sensitive(w, TRUE);
1189 } else {
1190 gtk_action_set_sensitive(w, FALSE);
1191 }
1192 } else {
1193 gtk_action_set_sensitive(w, FALSE);
1194 }
1195 }
1197 {
1198 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1199 SPItem *item = selection->singleItem();
1200 if (item && item->clip_ref && item->clip_ref->getObject()) {
1201 gtk_action_set_sensitive(w, TRUE);
1202 } else {
1203 gtk_action_set_sensitive(w, FALSE);
1204 }
1205 }
1207 {
1208 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1209 SPItem *item = selection->singleItem();
1210 if (item && item->mask_ref && item->mask_ref->getObject()) {
1211 gtk_action_set_sensitive(w, TRUE);
1212 } else {
1213 gtk_action_set_sensitive(w, FALSE);
1214 }
1215 }
1216 }
1218 void
1219 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1220 {
1221 sp_node_toolbox_sel_changed (selection, tbl);
1222 }
1226 //################################
1227 //## Node Editing Toolbox ##
1228 //################################
1230 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1231 {
1232 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1233 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1234 g_object_set_data( holder, "tracker", tracker );
1236 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1238 {
1239 InkAction* inky = ink_action_new( "NodeInsertAction",
1240 _("Insert node"),
1241 _("Insert new nodes into selected segments"),
1242 "node_insert",
1243 secondarySize );
1244 g_object_set( inky, "short_label", _("Insert"), NULL );
1245 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1246 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1247 }
1249 {
1250 InkAction* inky = ink_action_new( "NodeDeleteAction",
1251 _("Delete node"),
1252 _("Delete selected nodes"),
1253 "node_delete",
1254 secondarySize );
1255 g_object_set( inky, "short_label", _("Delete"), NULL );
1256 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1257 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1258 }
1260 {
1261 InkAction* inky = ink_action_new( "NodeJoinAction",
1262 _("Join endnodes"),
1263 _("Join selected endnodes"),
1264 "node_join",
1265 secondarySize );
1266 g_object_set( inky, "short_label", _("Join"), NULL );
1267 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1268 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1269 }
1271 {
1272 InkAction* inky = ink_action_new( "NodeBreakAction",
1273 _("Break nodes"),
1274 _("Break path at selected nodes"),
1275 "node_break",
1276 secondarySize );
1277 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1278 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1279 }
1282 {
1283 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1284 _("Join with segment"),
1285 _("Join selected endnodes with a new segment"),
1286 "node_join_segment",
1287 secondarySize );
1288 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1289 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1290 }
1292 {
1293 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1294 _("Delete segment"),
1295 _("Delete segment between two non-endpoint nodes"),
1296 "node_delete_segment",
1297 secondarySize );
1298 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1299 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1300 }
1302 {
1303 InkAction* inky = ink_action_new( "NodeCuspAction",
1304 _("Node Cusp"),
1305 _("Make selected nodes corner"),
1306 "node_cusp",
1307 secondarySize );
1308 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1309 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1310 }
1312 {
1313 InkAction* inky = ink_action_new( "NodeSmoothAction",
1314 _("Node Smooth"),
1315 _("Make selected nodes smooth"),
1316 "node_smooth",
1317 secondarySize );
1318 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1319 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1320 }
1322 {
1323 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1324 _("Node Symmetric"),
1325 _("Make selected nodes symmetric"),
1326 "node_symmetric",
1327 secondarySize );
1328 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1329 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1330 }
1332 {
1333 InkAction* inky = ink_action_new( "NodeLineAction",
1334 _("Node Line"),
1335 _("Make selected segments lines"),
1336 "node_line",
1337 secondarySize );
1338 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1339 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1340 }
1342 {
1343 InkAction* inky = ink_action_new( "NodeCurveAction",
1344 _("Node Curve"),
1345 _("Make selected segments curves"),
1346 "node_curve",
1347 secondarySize );
1348 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1349 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1350 }
1352 {
1353 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1354 _("Show Handles"),
1355 _("Show the Bezier handles of selected nodes"),
1356 "nodes_show_handles",
1357 Inkscape::ICON_SIZE_DECORATION );
1358 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1359 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1360 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1361 }
1363 {
1364 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1365 _("Show Outline"),
1366 _("Show the outline of the path"),
1367 "nodes_show_helperpath",
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_helperpath), desktop );
1371 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1372 }
1374 {
1375 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1376 _("Next path effect parameter"),
1377 _("Show next path effect parameter for editing"),
1378 "edit_next_parameter",
1379 Inkscape::ICON_SIZE_DECORATION );
1380 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1381 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1382 g_object_set_data( holder, "nodes_lpeedit", inky);
1383 }
1385 {
1386 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1387 _("Edit clipping path"),
1388 _("Edit the clipping path of the object"),
1389 "nodeedit-clippath",
1390 Inkscape::ICON_SIZE_DECORATION );
1391 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1392 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1393 g_object_set_data( holder, "nodes_clippathedit", inky);
1394 }
1396 {
1397 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1398 _("Edit mask path"),
1399 _("Edit the mask of the object"),
1400 "nodeedit-mask",
1401 Inkscape::ICON_SIZE_DECORATION );
1402 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1403 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404 g_object_set_data( holder, "nodes_maskedit", inky);
1405 }
1407 /* X coord of selected node(s) */
1408 {
1409 EgeAdjustmentAction* eact = 0;
1410 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1411 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1412 eact = create_adjustment_action( "NodeXAction",
1413 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1414 "tools.nodes", "Xcoord", 0,
1415 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1416 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1417 labels, values, G_N_ELEMENTS(labels),
1418 sp_node_path_x_value_changed );
1419 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1420 g_object_set_data( holder, "nodes_x_action", eact );
1421 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1422 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1423 }
1425 /* Y coord of selected node(s) */
1426 {
1427 EgeAdjustmentAction* eact = 0;
1428 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1429 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1430 eact = create_adjustment_action( "NodeYAction",
1431 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1432 "tools.nodes", "Ycoord", 0,
1433 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1434 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1435 labels, values, G_N_ELEMENTS(labels),
1436 sp_node_path_y_value_changed );
1437 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1438 g_object_set_data( holder, "nodes_y_action", eact );
1439 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1440 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1441 }
1443 // add the units menu
1444 {
1445 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1446 gtk_action_group_add_action( mainActions, act );
1447 }
1450 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1452 //watch selection
1453 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1455 sigc::connection *c_selection_changed =
1456 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1457 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1458 pool->add_connection ("selection-changed", c_selection_changed);
1460 sigc::connection *c_selection_modified =
1461 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1462 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1463 pool->add_connection ("selection-modified", c_selection_modified);
1465 sigc::connection *c_subselection_changed =
1466 new sigc::connection (desktop->connectToolSubselectionChanged
1467 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1468 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1470 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1472 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1473 } // end of sp_node_toolbox_prep()
1476 //########################
1477 //## Zoom Toolbox ##
1478 //########################
1480 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1481 {
1482 // no custom GtkAction setup needed
1483 } // end of sp_zoom_toolbox_prep()
1485 void
1486 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1487 {
1488 toolbox_set_desktop(toolbox,
1489 desktop,
1490 setup_tool_toolbox,
1491 update_tool_toolbox,
1492 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1493 "event_context_connection")));
1494 }
1497 void
1498 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1499 {
1500 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1501 desktop,
1502 setup_aux_toolbox,
1503 update_aux_toolbox,
1504 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1505 "event_context_connection")));
1506 }
1508 void
1509 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1510 {
1511 toolbox_set_desktop(toolbox,
1512 desktop,
1513 setup_commands_toolbox,
1514 update_commands_toolbox,
1515 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1516 "event_context_connection")));
1517 }
1519 static void
1520 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1521 {
1522 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1523 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1525 if (old_desktop) {
1526 GList *children, *iter;
1528 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1529 for ( iter = children ; iter ; iter = iter->next ) {
1530 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1531 }
1532 g_list_free(children);
1533 }
1535 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1537 if (desktop) {
1538 gtk_widget_set_sensitive(toolbox, TRUE);
1539 setup_func(toolbox, desktop);
1540 update_func(desktop, desktop->event_context, toolbox);
1541 *conn = desktop->connectEventContextChanged
1542 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1543 } else {
1544 gtk_widget_set_sensitive(toolbox, FALSE);
1545 }
1547 } // end of toolbox_set_desktop()
1550 static void
1551 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1552 {
1553 gchar const * descr =
1554 "<ui>"
1555 " <toolbar name='ToolToolbar'>"
1556 " <toolitem action='ToolSelector' />"
1557 " <toolitem action='ToolNode' />"
1558 " <toolitem action='ToolTweak' />"
1559 " <toolitem action='ToolZoom' />"
1560 " <toolitem action='ToolRect' />"
1561 " <toolitem action='Tool3DBox' />"
1562 " <toolitem action='ToolArc' />"
1563 " <toolitem action='ToolStar' />"
1564 " <toolitem action='ToolSpiral' />"
1565 " <toolitem action='ToolPencil' />"
1566 " <toolitem action='ToolPen' />"
1567 " <toolitem action='ToolCalligraphic' />"
1568 " <toolitem action='ToolEraser' />"
1569 " <toolitem action='ToolPaintBucket' />"
1570 " <toolitem action='ToolText' />"
1571 " <toolitem action='ToolConnector' />"
1572 " <toolitem action='ToolGradient' />"
1573 " <toolitem action='ToolDropper' />"
1574 " </toolbar>"
1575 "</ui>";
1576 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1577 GtkUIManager* mgr = gtk_ui_manager_new();
1578 GError* errVal = 0;
1580 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1581 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1583 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1584 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1585 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1586 }
1587 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1588 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1590 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1591 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1593 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1595 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1596 if ( child ) {
1597 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1598 }
1600 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1601 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1602 }
1605 static void
1606 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1607 {
1608 gchar const *const tname = ( eventcontext
1609 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1610 : NULL );
1611 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1613 for (int i = 0 ; tools[i].type_name ; i++ ) {
1614 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1615 if ( act ) {
1616 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1617 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1618 if ( verbAct ) {
1619 verbAct->set_active(setActive);
1620 }
1621 }
1622 }
1623 }
1625 static void
1626 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1627 {
1628 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1629 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1630 GtkUIManager* mgr = gtk_ui_manager_new();
1631 GError* errVal = 0;
1632 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1633 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1635 std::map<std::string, GtkWidget*> dataHolders;
1637 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1638 if ( aux_toolboxes[i].prep_func ) {
1639 // converted to GtkActions and UIManager
1641 GtkWidget* kludge = gtk_toolbar_new();
1642 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1643 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1644 dataHolders[aux_toolboxes[i].type_name] = kludge;
1645 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1646 } else {
1648 GtkWidget *sub_toolbox = 0;
1649 if (aux_toolboxes[i].create_func == NULL)
1650 sub_toolbox = sp_empty_toolbox_new(desktop);
1651 else {
1652 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1653 }
1655 gtk_size_group_add_widget( grouper, sub_toolbox );
1657 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1658 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1660 }
1661 }
1663 // Second pass to create toolbars *after* all GtkActions are created
1664 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1665 if ( aux_toolboxes[i].prep_func ) {
1666 // converted to GtkActions and UIManager
1668 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1670 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1671 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1673 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1674 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1675 g_free( tmp );
1676 tmp = 0;
1678 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1679 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1680 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1681 }
1682 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1685 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1687 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1688 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1689 swatch->setDesktop( desktop );
1690 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1691 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1692 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1693 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 );
1694 }
1696 gtk_widget_show_all( holder );
1697 sp_set_font_size_smaller( holder );
1699 gtk_size_group_add_widget( grouper, holder );
1701 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1702 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1703 }
1704 }
1706 g_object_unref( G_OBJECT(grouper) );
1707 }
1709 static void
1710 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1711 {
1712 gchar const *tname = ( eventcontext
1713 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1714 : NULL );
1715 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1716 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1717 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1718 gtk_widget_show_all(sub_toolbox);
1719 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1720 } else {
1721 gtk_widget_hide(sub_toolbox);
1722 }
1723 }
1724 }
1726 static void
1727 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1728 {
1729 gchar const * descr =
1730 "<ui>"
1731 " <toolbar name='CommandsToolbar'>"
1732 " <toolitem action='FileNew' />"
1733 " <toolitem action='FileOpen' />"
1734 " <toolitem action='FileSave' />"
1735 " <toolitem action='FilePrint' />"
1736 " <separator />"
1737 " <toolitem action='FileImport' />"
1738 " <toolitem action='FileExport' />"
1739 " <separator />"
1740 " <toolitem action='EditUndo' />"
1741 " <toolitem action='EditRedo' />"
1742 " <separator />"
1743 " <toolitem action='EditCopy' />"
1744 " <toolitem action='EditCut' />"
1745 " <toolitem action='EditPaste' />"
1746 " <separator />"
1747 " <toolitem action='ZoomSelection' />"
1748 " <toolitem action='ZoomDrawing' />"
1749 " <toolitem action='ZoomPage' />"
1750 " <separator />"
1751 " <toolitem action='EditDuplicate' />"
1752 " <toolitem action='EditClone' />"
1753 " <toolitem action='EditUnlinkClone' />"
1754 " <separator />"
1755 " <toolitem action='SelectionGroup' />"
1756 " <toolitem action='SelectionUnGroup' />"
1757 " <separator />"
1758 " <toolitem action='DialogFillStroke' />"
1759 " <toolitem action='DialogText' />"
1760 " <toolitem action='DialogXMLEditor' />"
1761 " <toolitem action='DialogAlignDistribute' />"
1762 " <separator />"
1763 " <toolitem action='DialogPreferences' />"
1764 " <toolitem action='DialogDocumentProperties' />"
1765 " </toolbar>"
1766 "</ui>";
1767 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1770 GtkUIManager* mgr = gtk_ui_manager_new();
1771 GError* errVal = 0;
1773 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1774 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1776 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1777 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1778 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1779 }
1781 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1782 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1784 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1785 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1788 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1790 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1791 if ( child ) {
1792 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1793 }
1795 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1796 }
1798 static void
1799 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1800 {
1801 }
1803 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1804 {
1805 gtk_widget_show(toolbox_toplevel);
1806 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1808 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1809 if (!shown_toolbox) {
1810 return;
1811 }
1812 gtk_widget_show(toolbox);
1814 gtk_widget_show_all(shown_toolbox);
1815 }
1817 static GtkWidget *
1818 sp_empty_toolbox_new(SPDesktop *desktop)
1819 {
1820 GtkWidget *tbl = gtk_toolbar_new();
1821 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1822 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1824 gtk_widget_show_all(tbl);
1825 sp_set_font_size_smaller (tbl);
1827 return tbl;
1828 }
1830 #define MODE_LABEL_WIDTH 70
1832 //########################
1833 //## Star ##
1834 //########################
1836 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1837 {
1838 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1840 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1841 // do not remember prefs if this call is initiated by an undo change, because undoing object
1842 // creation sets bogus values to its attributes before it is deleted
1843 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1844 }
1846 // quit if run by the attr_changed listener
1847 if (g_object_get_data( dataKludge, "freeze" )) {
1848 return;
1849 }
1851 // in turn, prevent listener from responding
1852 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1854 bool modmade = false;
1856 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1857 GSList const *items = selection->itemList();
1858 for (; items != NULL; items = items->next) {
1859 if (SP_IS_STAR((SPItem *) items->data)) {
1860 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1861 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1862 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1863 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1864 + M_PI / (gint)adj->value));
1865 SP_OBJECT((SPItem *) items->data)->updateRepr();
1866 modmade = true;
1867 }
1868 }
1869 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1870 _("Star: Change number of corners"));
1872 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1873 }
1875 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1876 {
1877 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1879 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1880 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1881 }
1883 // quit if run by the attr_changed listener
1884 if (g_object_get_data( dataKludge, "freeze" )) {
1885 return;
1886 }
1888 // in turn, prevent listener from responding
1889 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1891 bool modmade = false;
1892 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1893 GSList const *items = selection->itemList();
1894 for (; items != NULL; items = items->next) {
1895 if (SP_IS_STAR((SPItem *) items->data)) {
1896 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1898 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1899 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1900 if (r2 < r1) {
1901 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1902 } else {
1903 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1904 }
1906 SP_OBJECT((SPItem *) items->data)->updateRepr();
1907 modmade = true;
1908 }
1909 }
1911 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1912 _("Star: Change spoke ratio"));
1914 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1915 }
1917 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1918 {
1919 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1920 bool flat = ege_select_one_action_get_active( act ) == 0;
1922 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1923 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1924 flat ? "true" : "false" );
1925 }
1927 // quit if run by the attr_changed listener
1928 if (g_object_get_data( dataKludge, "freeze" )) {
1929 return;
1930 }
1932 // in turn, prevent listener from responding
1933 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1935 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1936 GSList const *items = selection->itemList();
1937 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1938 bool modmade = false;
1940 if ( prop_action ) {
1941 gtk_action_set_sensitive( prop_action, !flat );
1942 }
1944 for (; items != NULL; items = items->next) {
1945 if (SP_IS_STAR((SPItem *) items->data)) {
1946 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1947 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1948 SP_OBJECT((SPItem *) items->data)->updateRepr();
1949 modmade = true;
1950 }
1951 }
1953 if (modmade) {
1954 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1955 flat ? _("Make polygon") : _("Make star"));
1956 }
1958 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1959 }
1961 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1962 {
1963 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1965 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1966 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1967 }
1969 // quit if run by the attr_changed listener
1970 if (g_object_get_data( dataKludge, "freeze" )) {
1971 return;
1972 }
1974 // in turn, prevent listener from responding
1975 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1977 bool modmade = false;
1979 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1980 GSList const *items = selection->itemList();
1981 for (; items != NULL; items = items->next) {
1982 if (SP_IS_STAR((SPItem *) items->data)) {
1983 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1984 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1985 SP_OBJECT(items->data)->updateRepr();
1986 modmade = true;
1987 }
1988 }
1989 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1990 _("Star: Change rounding"));
1992 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1993 }
1995 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1996 {
1997 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1999 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2000 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2001 }
2003 // quit if run by the attr_changed listener
2004 if (g_object_get_data( dataKludge, "freeze" )) {
2005 return;
2006 }
2008 // in turn, prevent listener from responding
2009 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2011 bool modmade = false;
2013 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2014 GSList const *items = selection->itemList();
2015 for (; items != NULL; items = items->next) {
2016 if (SP_IS_STAR((SPItem *) items->data)) {
2017 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2018 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2019 SP_OBJECT(items->data)->updateRepr();
2020 modmade = true;
2021 }
2022 }
2023 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2024 _("Star: Change randomization"));
2026 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2027 }
2030 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2031 gchar const */*old_value*/, gchar const */*new_value*/,
2032 bool /*is_interactive*/, gpointer data)
2033 {
2034 GtkWidget *tbl = GTK_WIDGET(data);
2036 // quit if run by the _changed callbacks
2037 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2038 return;
2039 }
2041 // in turn, prevent callbacks from responding
2042 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2044 GtkAdjustment *adj = 0;
2046 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2047 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2049 if (!strcmp(name, "inkscape:randomized")) {
2050 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2051 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2052 } else if (!strcmp(name, "inkscape:rounded")) {
2053 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2054 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2055 } else if (!strcmp(name, "inkscape:flatsided")) {
2056 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2057 char const *flatsides = repr->attribute("inkscape:flatsided");
2058 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2059 if ( flatsides && !strcmp(flatsides,"false") ) {
2060 ege_select_one_action_set_active( flat_action, 1 );
2061 gtk_action_set_sensitive( prop_action, TRUE );
2062 } else {
2063 ege_select_one_action_set_active( flat_action, 0 );
2064 gtk_action_set_sensitive( prop_action, FALSE );
2065 }
2066 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2067 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2068 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2069 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2070 if (r2 < r1) {
2071 gtk_adjustment_set_value(adj, r2/r1);
2072 } else {
2073 gtk_adjustment_set_value(adj, r1/r2);
2074 }
2075 } else if (!strcmp(name, "sodipodi:sides")) {
2076 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2077 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2078 }
2080 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2081 }
2084 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2085 {
2086 NULL, /* child_added */
2087 NULL, /* child_removed */
2088 star_tb_event_attr_changed,
2089 NULL, /* content_changed */
2090 NULL /* order_changed */
2091 };
2094 /**
2095 * \param selection Should not be NULL.
2096 */
2097 static void
2098 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2099 {
2100 int n_selected = 0;
2101 Inkscape::XML::Node *repr = NULL;
2103 purge_repr_listener( tbl, tbl );
2105 for (GSList const *items = selection->itemList();
2106 items != NULL;
2107 items = items->next)
2108 {
2109 if (SP_IS_STAR((SPItem *) items->data)) {
2110 n_selected++;
2111 repr = SP_OBJECT_REPR((SPItem *) items->data);
2112 }
2113 }
2115 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2117 if (n_selected == 0) {
2118 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2119 } else if (n_selected == 1) {
2120 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2122 if (repr) {
2123 g_object_set_data( tbl, "repr", repr );
2124 Inkscape::GC::anchor(repr);
2125 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2126 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2127 }
2128 } else {
2129 // FIXME: implement averaging of all parameters for multiple selected stars
2130 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2131 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2132 }
2133 }
2136 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2137 {
2138 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2139 // callbacks to lump all the changes for all selected objects in one undo step
2141 GtkAdjustment *adj = 0;
2143 // fixme: make settable in prefs!
2144 gint mag = 5;
2145 gdouble prop = 0.5;
2146 gboolean flat = FALSE;
2147 gdouble randomized = 0;
2148 gdouble rounded = 0;
2150 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2151 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2153 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2154 gtk_action_set_sensitive( sb2, !flat );
2156 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2157 gtk_adjustment_set_value(adj, mag);
2158 gtk_adjustment_value_changed(adj);
2160 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2161 gtk_adjustment_set_value(adj, prop);
2162 gtk_adjustment_value_changed(adj);
2164 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2165 gtk_adjustment_set_value(adj, rounded);
2166 gtk_adjustment_value_changed(adj);
2168 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2169 gtk_adjustment_set_value(adj, randomized);
2170 gtk_adjustment_value_changed(adj);
2171 }
2174 void
2175 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2176 {
2177 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2178 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2179 GtkWidget *l = gtk_label_new(NULL);
2180 gtk_label_set_markup(GTK_LABEL(l), title);
2181 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2182 if ( GTK_IS_TOOLBAR(tbl) ) {
2183 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2184 } else {
2185 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2186 }
2187 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2188 }
2191 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2192 {
2193 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2195 {
2196 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2197 ege_output_action_set_use_markup( act, TRUE );
2198 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2199 g_object_set_data( holder, "mode_action", act );
2200 }
2202 {
2203 EgeAdjustmentAction* eact = 0;
2204 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2205 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2207 /* Flatsided checkbox */
2208 {
2209 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2211 GtkTreeIter iter;
2212 gtk_list_store_append( model, &iter );
2213 gtk_list_store_set( model, &iter,
2214 0, _("Polygon"),
2215 1, _("Regular polygon (with one handle) instead of a star"),
2216 2, "star_flat",
2217 -1 );
2219 gtk_list_store_append( model, &iter );
2220 gtk_list_store_set( model, &iter,
2221 0, _("Star"),
2222 1, _("Star instead of a regular polygon (with one handle)"),
2223 2, "star_angled",
2224 -1 );
2226 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2227 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2228 g_object_set_data( holder, "flat_action", act );
2230 ege_select_one_action_set_appearance( act, "full" );
2231 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2232 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2233 ege_select_one_action_set_icon_column( act, 2 );
2234 ege_select_one_action_set_icon_size( act, secondarySize );
2235 ege_select_one_action_set_tooltip_column( act, 1 );
2237 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2238 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2239 }
2241 /* Magnitude */
2242 {
2243 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2244 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2245 eact = create_adjustment_action( "MagnitudeAction",
2246 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2247 "tools.shapes.star", "magnitude", 3,
2248 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2249 3, 1024, 1, 5,
2250 labels, values, G_N_ELEMENTS(labels),
2251 sp_stb_magnitude_value_changed,
2252 1.0, 0 );
2253 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2254 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2255 }
2257 /* Spoke ratio */
2258 {
2259 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2260 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2261 eact = create_adjustment_action( "SpokeAction",
2262 _("Spoke ratio"), _("Spoke ratio:"),
2263 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2264 // Base radius is the same for the closest handle.
2265 _("Base radius to tip radius ratio"),
2266 "tools.shapes.star", "proportion", 0.5,
2267 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2268 0.01, 1.0, 0.01, 0.1,
2269 labels, values, G_N_ELEMENTS(labels),
2270 sp_stb_proportion_value_changed );
2271 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2272 g_object_set_data( holder, "prop_action", eact );
2273 }
2275 if ( !isFlatSided ) {
2276 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2277 } else {
2278 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2279 }
2281 /* Roundedness */
2282 {
2283 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2284 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2285 eact = create_adjustment_action( "RoundednessAction",
2286 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2287 "tools.shapes.star", "rounded", 0.0,
2288 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2289 -10.0, 10.0, 0.01, 0.1,
2290 labels, values, G_N_ELEMENTS(labels),
2291 sp_stb_rounded_value_changed );
2292 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2293 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2294 }
2296 /* Randomization */
2297 {
2298 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2299 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2300 eact = create_adjustment_action( "RandomizationAction",
2301 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2302 "tools.shapes.star", "randomized", 0.0,
2303 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2304 -10.0, 10.0, 0.001, 0.01,
2305 labels, values, G_N_ELEMENTS(labels),
2306 sp_stb_randomized_value_changed, 0.1, 3 );
2307 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2308 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2309 }
2310 }
2312 {
2313 /* Reset */
2314 {
2315 GtkAction* act = gtk_action_new( "StarResetAction",
2316 _("Defaults"),
2317 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2318 GTK_STOCK_CLEAR );
2319 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2320 gtk_action_group_add_action( mainActions, act );
2321 gtk_action_set_sensitive( act, TRUE );
2322 }
2323 }
2325 sigc::connection *connection = new sigc::connection(
2326 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2327 );
2328 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2329 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2330 }
2333 //########################
2334 //## Rect ##
2335 //########################
2337 static void sp_rtb_sensitivize( GObject *tbl )
2338 {
2339 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2340 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2341 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2343 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2344 gtk_action_set_sensitive( not_rounded, FALSE );
2345 } else {
2346 gtk_action_set_sensitive( not_rounded, TRUE );
2347 }
2348 }
2351 static void
2352 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2353 void (*setter)(SPRect *, gdouble))
2354 {
2355 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2357 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2358 SPUnit const *unit = tracker->getActiveUnit();
2360 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2361 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2362 }
2364 // quit if run by the attr_changed listener
2365 if (g_object_get_data( tbl, "freeze" )) {
2366 return;
2367 }
2369 // in turn, prevent listener from responding
2370 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2372 bool modmade = false;
2373 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2374 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2375 if (SP_IS_RECT(items->data)) {
2376 if (adj->value != 0) {
2377 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2378 } else {
2379 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2380 }
2381 modmade = true;
2382 }
2383 }
2385 sp_rtb_sensitivize( tbl );
2387 if (modmade) {
2388 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2389 _("Change rectangle"));
2390 }
2392 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2393 }
2395 static void
2396 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2397 {
2398 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2399 }
2401 static void
2402 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2403 {
2404 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2405 }
2407 static void
2408 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2409 {
2410 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2411 }
2413 static void
2414 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2415 {
2416 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2417 }
2421 static void
2422 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2423 {
2424 GtkAdjustment *adj = 0;
2426 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2427 gtk_adjustment_set_value(adj, 0.0);
2428 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2429 gtk_adjustment_value_changed(adj);
2431 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2432 gtk_adjustment_set_value(adj, 0.0);
2433 gtk_adjustment_value_changed(adj);
2435 sp_rtb_sensitivize( obj );
2436 }
2438 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2439 gchar const */*old_value*/, gchar const */*new_value*/,
2440 bool /*is_interactive*/, gpointer data)
2441 {
2442 GObject *tbl = G_OBJECT(data);
2444 // quit if run by the _changed callbacks
2445 if (g_object_get_data( tbl, "freeze" )) {
2446 return;
2447 }
2449 // in turn, prevent callbacks from responding
2450 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2452 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2453 SPUnit const *unit = tracker->getActiveUnit();
2455 gpointer item = g_object_get_data( tbl, "item" );
2456 if (item && SP_IS_RECT(item)) {
2457 {
2458 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2459 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2460 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2461 }
2463 {
2464 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2465 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2466 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2467 }
2469 {
2470 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2471 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2472 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2473 }
2475 {
2476 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2477 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2478 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2479 }
2480 }
2482 sp_rtb_sensitivize( tbl );
2484 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2485 }
2488 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2489 NULL, /* child_added */
2490 NULL, /* child_removed */
2491 rect_tb_event_attr_changed,
2492 NULL, /* content_changed */
2493 NULL /* order_changed */
2494 };
2496 /**
2497 * \param selection should not be NULL.
2498 */
2499 static void
2500 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2501 {
2502 int n_selected = 0;
2503 Inkscape::XML::Node *repr = NULL;
2504 SPItem *item = NULL;
2506 if ( g_object_get_data( tbl, "repr" ) ) {
2507 g_object_set_data( tbl, "item", NULL );
2508 }
2509 purge_repr_listener( tbl, tbl );
2511 for (GSList const *items = selection->itemList();
2512 items != NULL;
2513 items = items->next) {
2514 if (SP_IS_RECT((SPItem *) items->data)) {
2515 n_selected++;
2516 item = (SPItem *) items->data;
2517 repr = SP_OBJECT_REPR(item);
2518 }
2519 }
2521 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2523 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2525 if (n_selected == 0) {
2526 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2528 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2529 gtk_action_set_sensitive(w, FALSE);
2530 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2531 gtk_action_set_sensitive(h, FALSE);
2533 } else if (n_selected == 1) {
2534 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2535 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2537 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2538 gtk_action_set_sensitive(w, TRUE);
2539 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2540 gtk_action_set_sensitive(h, TRUE);
2542 if (repr) {
2543 g_object_set_data( tbl, "repr", repr );
2544 g_object_set_data( tbl, "item", item );
2545 Inkscape::GC::anchor(repr);
2546 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2547 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2548 }
2549 } else {
2550 // FIXME: implement averaging of all parameters for multiple selected
2551 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2552 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2553 sp_rtb_sensitivize( tbl );
2554 }
2555 }
2558 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2559 {
2560 EgeAdjustmentAction* eact = 0;
2561 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2563 {
2564 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2565 ege_output_action_set_use_markup( act, TRUE );
2566 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2567 g_object_set_data( holder, "mode_action", act );
2568 }
2570 // rx/ry units menu: create
2571 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2572 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2573 // fixme: add % meaning per cent of the width/height
2574 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2575 g_object_set_data( holder, "tracker", tracker );
2577 /* W */
2578 {
2579 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2580 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2581 eact = create_adjustment_action( "RectWidthAction",
2582 _("Width"), _("W:"), _("Width of rectangle"),
2583 "tools.shapes.rect", "width", 0,
2584 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2585 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2586 labels, values, G_N_ELEMENTS(labels),
2587 sp_rtb_width_value_changed );
2588 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2589 g_object_set_data( holder, "width_action", eact );
2590 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2591 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2592 }
2594 /* H */
2595 {
2596 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2597 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2598 eact = create_adjustment_action( "RectHeightAction",
2599 _("Height"), _("H:"), _("Height of rectangle"),
2600 "tools.shapes.rect", "height", 0,
2601 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2602 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2603 labels, values, G_N_ELEMENTS(labels),
2604 sp_rtb_height_value_changed );
2605 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2606 g_object_set_data( holder, "height_action", eact );
2607 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2608 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2609 }
2611 /* rx */
2612 {
2613 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2614 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2615 eact = create_adjustment_action( "RadiusXAction",
2616 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2617 "tools.shapes.rect", "rx", 0,
2618 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2619 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2620 labels, values, G_N_ELEMENTS(labels),
2621 sp_rtb_rx_value_changed);
2622 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2623 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2624 }
2626 /* ry */
2627 {
2628 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2629 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2630 eact = create_adjustment_action( "RadiusYAction",
2631 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2632 "tools.shapes.rect", "ry", 0,
2633 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2634 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2635 labels, values, G_N_ELEMENTS(labels),
2636 sp_rtb_ry_value_changed);
2637 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2638 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2639 }
2641 // add the units menu
2642 {
2643 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2644 gtk_action_group_add_action( mainActions, act );
2645 }
2647 /* Reset */
2648 {
2649 InkAction* inky = ink_action_new( "RectResetAction",
2650 _("Not rounded"),
2651 _("Make corners sharp"),
2652 "squared_corner",
2653 secondarySize );
2654 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2655 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2656 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2657 g_object_set_data( holder, "not_rounded", inky );
2658 }
2660 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2661 sp_rtb_sensitivize( holder );
2663 sigc::connection *connection = new sigc::connection(
2664 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2665 );
2666 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2667 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2668 }
2670 //########################
2671 //## 3D Box ##
2672 //########################
2674 // normalize angle so that it lies in the interval [0,360]
2675 static double box3d_normalize_angle (double a) {
2676 double angle = a + ((int) (a/360.0))*360;
2677 if (angle < 0) {
2678 angle += 360.0;
2679 }
2680 return angle;
2681 }
2683 static void
2684 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2685 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2686 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2687 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2688 // are reset).
2689 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2691 if (is_infinite) {
2692 gtk_toggle_action_set_active(tact, TRUE);
2693 gtk_action_set_sensitive(act, TRUE);
2695 double angle = persp3d_get_infinite_angle(persp, axis);
2696 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2697 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2698 }
2699 } else {
2700 gtk_toggle_action_set_active(tact, FALSE);
2701 gtk_action_set_sensitive(act, FALSE);
2702 }
2703 }
2705 static void
2706 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2707 if (!persp_repr) {
2708 g_print ("No perspective given to box3d_resync_toolbar().\n");
2709 return;
2710 }
2712 GtkWidget *tbl = GTK_WIDGET(data);
2713 GtkAdjustment *adj = 0;
2714 GtkAction *act = 0;
2715 GtkToggleAction *tact = 0;
2716 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2717 {
2718 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2719 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2720 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2722 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2723 }
2724 {
2725 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2726 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2727 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2729 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2730 }
2731 {
2732 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2733 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2734 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2736 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2737 }
2738 }
2740 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2741 gchar const */*old_value*/, gchar const */*new_value*/,
2742 bool /*is_interactive*/, gpointer data)
2743 {
2744 GtkWidget *tbl = GTK_WIDGET(data);
2746 // quit if run by the attr_changed listener
2747 // note: it used to work without the differently called freeze_ attributes (here and in
2748 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2749 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2750 return;
2751 }
2753 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2754 // sp_document_maybe_done() when the document is undo insensitive)
2755 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2757 // TODO: Only update the appropriate part of the toolbar
2758 // if (!strcmp(name, "inkscape:vp_z")) {
2759 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2760 // }
2762 Persp3D *persp = persp3d_get_from_repr(repr);
2763 persp3d_update_box_reprs(persp);
2765 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2766 }
2768 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2769 {
2770 NULL, /* child_added */
2771 NULL, /* child_removed */
2772 box3d_persp_tb_event_attr_changed,
2773 NULL, /* content_changed */
2774 NULL /* order_changed */
2775 };
2777 /**
2778 * \param selection Should not be NULL.
2779 */
2780 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2781 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2782 static void
2783 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2784 {
2785 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2786 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2787 // update the perspectives with infinite VPs and leave the other ones untouched).
2789 Inkscape::XML::Node *persp_repr = NULL;
2790 purge_repr_listener(tbl, tbl);
2792 SPItem *item = selection->singleItem();
2793 if (item && SP_IS_BOX3D(item)) {
2794 // FIXME: Also deal with multiple selected boxes
2795 SPBox3D *box = SP_BOX3D(item);
2796 Persp3D *persp = box3d_get_perspective(box);
2797 persp_repr = SP_OBJECT_REPR(persp);
2798 if (persp_repr) {
2799 g_object_set_data(tbl, "repr", persp_repr);
2800 Inkscape::GC::anchor(persp_repr);
2801 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2802 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2803 }
2805 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2806 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2808 box3d_resync_toolbar(persp_repr, tbl);
2809 }
2810 }
2812 static void
2813 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2814 {
2815 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2816 SPDocument *document = sp_desktop_document(desktop);
2818 // quit if run by the attr_changed listener
2819 // note: it used to work without the differently called freeze_ attributes (here and in
2820 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2821 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2822 return;
2823 }
2825 // in turn, prevent listener from responding
2826 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2828 //Persp3D *persp = document->current_persp3d;
2829 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2830 if (sel_persps.empty()) {
2831 // this can happen when the document is created; we silently ignore it
2832 return;
2833 }
2834 Persp3D *persp = sel_persps.front();
2836 persp->tmat.set_infinite_direction (axis, adj->value);
2837 SP_OBJECT(persp)->updateRepr();
2839 // TODO: use the correct axis here, too
2840 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2842 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2843 }
2846 static void
2847 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2848 {
2849 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2850 }
2852 static void
2853 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2854 {
2855 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2856 }
2858 static void
2859 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2860 {
2861 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2862 }
2865 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2866 {
2867 // TODO: Take all selected perspectives into account
2868 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2869 if (sel_persps.empty()) {
2870 // this can happen when the document is created; we silently ignore it
2871 return;
2872 }
2873 Persp3D *persp = sel_persps.front();
2875 bool set_infinite = gtk_toggle_action_get_active(act);
2876 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2877 }
2879 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2880 {
2881 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2882 }
2884 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2885 {
2886 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2887 }
2889 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2890 {
2891 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2892 }
2894 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2895 {
2896 EgeAdjustmentAction* eact = 0;
2897 SPDocument *document = sp_desktop_document (desktop);
2898 Persp3D *persp = document->current_persp3d;
2900 EgeAdjustmentAction* box3d_angle_x = 0;
2901 EgeAdjustmentAction* box3d_angle_y = 0;
2902 EgeAdjustmentAction* box3d_angle_z = 0;
2904 /* Angle X */
2905 {
2906 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2907 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2908 eact = create_adjustment_action( "3DBoxAngleXAction",
2909 _("Angle in X direction"), _("Angle X:"),
2910 // Translators: PL is short for 'perspective line'
2911 _("Angle of PLs in X direction"),
2912 "tools.shapes.3dbox", "box3d_angle_x", 30,
2913 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2914 -360.0, 360.0, 1.0, 10.0,
2915 labels, values, G_N_ELEMENTS(labels),
2916 box3d_angle_x_value_changed );
2917 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2918 g_object_set_data( holder, "box3d_angle_x_action", eact );
2919 box3d_angle_x = eact;
2920 }
2922 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2923 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2924 } else {
2925 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2926 }
2929 /* VP X state */
2930 {
2931 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2932 // Translators: VP is short for 'vanishing point'
2933 _("State of VP in X direction"),
2934 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2935 "toggle_vp_x",
2936 Inkscape::ICON_SIZE_DECORATION );
2937 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2938 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2939 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2940 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2941 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2942 }
2944 /* Angle Y */
2945 {
2946 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2947 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2948 eact = create_adjustment_action( "3DBoxAngleYAction",
2949 _("Angle in Y direction"), _("Angle Y:"),
2950 // Translators: PL is short for 'perspective line'
2951 _("Angle of PLs in Y direction"),
2952 "tools.shapes.3dbox", "box3d_angle_y", 30,
2953 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2954 -360.0, 360.0, 1.0, 10.0,
2955 labels, values, G_N_ELEMENTS(labels),
2956 box3d_angle_y_value_changed );
2957 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2958 g_object_set_data( holder, "box3d_angle_y_action", eact );
2959 box3d_angle_y = eact;
2960 }
2962 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2963 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2964 } else {
2965 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2966 }
2968 /* VP Y state */
2969 {
2970 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2971 // Translators: VP is short for 'vanishing point'
2972 _("State of VP in Y direction"),
2973 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2974 "toggle_vp_y",
2975 Inkscape::ICON_SIZE_DECORATION );
2976 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2977 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2978 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2979 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2980 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2981 }
2983 /* Angle Z */
2984 {
2985 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2986 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2987 eact = create_adjustment_action( "3DBoxAngleZAction",
2988 _("Angle in Z direction"), _("Angle Z:"),
2989 // Translators: PL is short for 'perspective line'
2990 _("Angle of PLs in Z direction"),
2991 "tools.shapes.3dbox", "box3d_angle_z", 30,
2992 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2993 -360.0, 360.0, 1.0, 10.0,
2994 labels, values, G_N_ELEMENTS(labels),
2995 box3d_angle_z_value_changed );
2996 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2997 g_object_set_data( holder, "box3d_angle_z_action", eact );
2998 box3d_angle_z = eact;
2999 }
3001 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3002 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3003 } else {
3004 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3005 }
3007 /* VP Z state */
3008 {
3009 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3010 // Translators: VP is short for 'vanishing point'
3011 _("State of VP in Z direction"),
3012 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3013 "toggle_vp_z",
3014 Inkscape::ICON_SIZE_DECORATION );
3015 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3016 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3017 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3018 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3019 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3020 }
3022 sigc::connection *connection = new sigc::connection(
3023 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3024 );
3025 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3026 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3027 }
3029 //########################
3030 //## Spiral ##
3031 //########################
3033 static void
3034 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3035 {
3036 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3038 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3039 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3040 }
3042 // quit if run by the attr_changed listener
3043 if (g_object_get_data( tbl, "freeze" )) {
3044 return;
3045 }
3047 // in turn, prevent listener from responding
3048 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3050 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3052 bool modmade = false;
3053 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3054 items != NULL;
3055 items = items->next)
3056 {
3057 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3058 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3059 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3060 SP_OBJECT((SPItem *) items->data)->updateRepr();
3061 modmade = true;
3062 }
3063 }
3065 g_free(namespaced_name);
3067 if (modmade) {
3068 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3069 _("Change spiral"));
3070 }
3072 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3073 }
3075 static void
3076 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3077 {
3078 sp_spl_tb_value_changed(adj, tbl, "revolution");
3079 }
3081 static void
3082 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3083 {
3084 sp_spl_tb_value_changed(adj, tbl, "expansion");
3085 }
3087 static void
3088 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3089 {
3090 sp_spl_tb_value_changed(adj, tbl, "t0");
3091 }
3093 static void
3094 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3095 {
3096 GtkWidget *tbl = GTK_WIDGET(obj);
3098 GtkAdjustment *adj;
3100 // fixme: make settable
3101 gdouble rev = 5;
3102 gdouble exp = 1.0;
3103 gdouble t0 = 0.0;
3105 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3106 gtk_adjustment_set_value(adj, rev);
3107 gtk_adjustment_value_changed(adj);
3109 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3110 gtk_adjustment_set_value(adj, exp);
3111 gtk_adjustment_value_changed(adj);
3113 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3114 gtk_adjustment_set_value(adj, t0);
3115 gtk_adjustment_value_changed(adj);
3117 spinbutton_defocus(GTK_OBJECT(tbl));
3118 }
3121 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3122 gchar const */*old_value*/, gchar const */*new_value*/,
3123 bool /*is_interactive*/, gpointer data)
3124 {
3125 GtkWidget *tbl = GTK_WIDGET(data);
3127 // quit if run by the _changed callbacks
3128 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3129 return;
3130 }
3132 // in turn, prevent callbacks from responding
3133 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3135 GtkAdjustment *adj;
3136 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3137 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3139 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3140 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3142 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3143 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3145 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3146 }
3149 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3150 NULL, /* child_added */
3151 NULL, /* child_removed */
3152 spiral_tb_event_attr_changed,
3153 NULL, /* content_changed */
3154 NULL /* order_changed */
3155 };
3157 static void
3158 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3159 {
3160 int n_selected = 0;
3161 Inkscape::XML::Node *repr = NULL;
3163 purge_repr_listener( tbl, tbl );
3165 for (GSList const *items = selection->itemList();
3166 items != NULL;
3167 items = items->next)
3168 {
3169 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3170 n_selected++;
3171 repr = SP_OBJECT_REPR((SPItem *) items->data);
3172 }
3173 }
3175 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3177 if (n_selected == 0) {
3178 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3179 } else if (n_selected == 1) {
3180 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3182 if (repr) {
3183 g_object_set_data( tbl, "repr", repr );
3184 Inkscape::GC::anchor(repr);
3185 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3186 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3187 }
3188 } else {
3189 // FIXME: implement averaging of all parameters for multiple selected
3190 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3191 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3192 }
3193 }
3196 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3197 {
3198 EgeAdjustmentAction* eact = 0;
3199 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3201 {
3202 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3203 ege_output_action_set_use_markup( act, TRUE );
3204 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3205 g_object_set_data( holder, "mode_action", act );
3206 }
3208 /* Revolution */
3209 {
3210 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3211 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3212 eact = create_adjustment_action( "SpiralRevolutionAction",
3213 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3214 "tools.shapes.spiral", "revolution", 3.0,
3215 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3216 0.01, 1024.0, 0.1, 1.0,
3217 labels, values, G_N_ELEMENTS(labels),
3218 sp_spl_tb_revolution_value_changed, 1, 2);
3219 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3220 }
3222 /* Expansion */
3223 {
3224 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3225 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3226 eact = create_adjustment_action( "SpiralExpansionAction",
3227 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3228 "tools.shapes.spiral", "expansion", 1.0,
3229 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3230 0.0, 1000.0, 0.01, 1.0,
3231 labels, values, G_N_ELEMENTS(labels),
3232 sp_spl_tb_expansion_value_changed);
3233 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3234 }
3236 /* T0 */
3237 {
3238 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3239 gdouble values[] = {0, 0.5, 0.9};
3240 eact = create_adjustment_action( "SpiralT0Action",
3241 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3242 "tools.shapes.spiral", "t0", 0.0,
3243 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3244 0.0, 0.999, 0.01, 1.0,
3245 labels, values, G_N_ELEMENTS(labels),
3246 sp_spl_tb_t0_value_changed);
3247 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3248 }
3250 /* Reset */
3251 {
3252 InkAction* inky = ink_action_new( "SpiralResetAction",
3253 _("Defaults"),
3254 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3255 GTK_STOCK_CLEAR,
3256 secondarySize );
3257 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3258 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3259 }
3262 sigc::connection *connection = new sigc::connection(
3263 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3264 );
3265 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3266 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3267 }
3269 //########################
3270 //## Pen/Pencil ##
3271 //########################
3273 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3274 {
3275 prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3276 }
3278 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3279 {
3280 // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3281 // adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3282 {
3283 EgeOutputAction* act = ege_output_action_new(
3284 tool_is_pencil ?
3285 "FreehandModeActionPencilTemp" :
3286 "FreehandModeActionPenTemp",
3287 _("<b>Mode:</b>"), "", 0 );
3288 ege_output_action_set_use_markup( act, TRUE );
3289 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3290 g_object_set_data( holder, "freehand_mode_action", act );
3291 }
3293 /* Freehand mode toggle buttons */
3294 {
3295 //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3296 //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3297 guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3298 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3300 {
3301 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3303 GtkTreeIter iter;
3304 gtk_list_store_append( model, &iter );
3305 gtk_list_store_set( model, &iter,
3306 0, _("Bézier"),
3307 1, _("Regular Bézier mode"),
3308 2, "bezier_mode",
3309 -1 );
3311 gtk_list_store_append( model, &iter );
3312 gtk_list_store_set( model, &iter,
3313 0, _("Spiro"),
3314 1, _("Spiro splines mode"),
3315 2, "spiro_splines_mode",
3316 -1 );
3318 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3319 "FreehandModeActionPencil" :
3320 "FreehandModeActionPen",
3321 (""), (""), NULL, GTK_TREE_MODEL(model) );
3322 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3323 g_object_set_data( holder, "freehande_mode_action", act );
3325 ege_select_one_action_set_appearance( act, "full" );
3326 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3327 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3328 ege_select_one_action_set_icon_column( act, 2 );
3329 ege_select_one_action_set_icon_size( act, secondarySize );
3330 ege_select_one_action_set_tooltip_column( act, 1 );
3332 ege_select_one_action_set_active( act, spiroMode);
3333 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3334 }
3335 }
3336 }
3338 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject */*dataKludge*/) {
3339 gint shape = ege_select_one_action_get_active( act );
3340 prefs_set_int_attribute("tools.freehand", "shape", shape);
3341 }
3343 /**
3344 * \brief Generate the list of freehand advanced shape option entries.
3345 */
3346 GList * freehand_shape_dropdown_items_list() {
3347 GList *glist = NULL;
3349 glist = g_list_append (glist, _("None"));
3350 glist = g_list_append (glist, _("Clipboard"));
3351 glist = g_list_append (glist, _("Crescendo"));
3352 glist = g_list_append (glist, _("Decrescendo"));
3354 return glist;
3355 }
3357 static void
3358 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3359 /*advanced shape options */
3360 {
3361 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3363 GList* items = 0;
3364 gint count = 0;
3365 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3366 {
3367 GtkTreeIter iter;
3368 gtk_list_store_append( model, &iter );
3369 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3370 count++;
3371 }
3372 g_list_free( items );
3373 items = 0;
3374 EgeSelectOneAction* act1 = ege_select_one_action_new(
3375 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3376 _("Shape:"), (""), NULL, GTK_TREE_MODEL(model));
3377 g_object_set( act1, "short_label", _("Shape:"), NULL );
3378 ege_select_one_action_set_appearance( act1, "compact" );
3379 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.freehand", "shape", 0) );
3380 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3381 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3382 g_object_set_data( holder, "shape_action", act1 );
3383 }
3384 }
3386 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3387 {
3388 sp_add_spiro_toggle(mainActions, holder, false);
3389 sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3390 }
3393 static void
3394 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3395 {
3396 GtkWidget *tbl = GTK_WIDGET(obj);
3398 GtkAdjustment *adj;
3400 // fixme: make settable
3401 gdouble tolerance = 4;
3403 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3404 gtk_adjustment_set_value(adj, tolerance);
3405 gtk_adjustment_value_changed(adj);
3407 spinbutton_defocus(GTK_OBJECT(tbl));
3408 }
3410 static void
3411 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3412 {
3414 // quit if run by the attr_changed listener
3415 if (g_object_get_data( tbl, "freeze" )) {
3416 return;
3417 }
3418 // in turn, prevent listener from responding
3419 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3420 prefs_set_double_attribute("tools.freehand.pencil",
3421 "tolerance", adj->value);
3422 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3424 }
3428 static void
3429 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3430 const gchar */*key*/,
3431 const gchar */*oldval*/,
3432 const gchar */*newval*/,
3433 bool /*is_interactive*/,
3434 void * data)
3435 {
3436 GObject* tbl = G_OBJECT(data);
3437 if (g_object_get_data( tbl, "freeze" )) {
3438 return;
3439 }
3441 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3443 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3444 "tolerance");
3446 double v = prefs_get_double_attribute("tools.freehand.pencil",
3447 "tolerance", adj->value);
3448 gtk_adjustment_set_value(adj, v);
3449 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3451 }
3453 static Inkscape::XML::NodeEventVector pencil_node_events =
3454 {
3455 NULL,
3456 NULL,
3457 sp_pencil_tb_tolerance_value_changed_external,
3458 NULL,
3459 NULL,
3460 };
3463 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3464 {
3465 sp_add_spiro_toggle(mainActions, holder, true);
3467 EgeAdjustmentAction* eact = 0;
3469 /* Tolerance */
3470 {
3471 gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3472 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3473 eact = create_adjustment_action( "PencilToleranceAction",
3474 _("Smoothing:"), _("Smoothing"),
3475 _("How much smoothing (simplifying) is applied to the line"),
3476 "tools.freehand.pencil", "tolerance",
3477 3.0,
3478 GTK_WIDGET(desktop->canvas), NULL,
3479 holder, TRUE, "altx-pencil",
3480 1, 100.0, 0.5, 0,
3481 labels, values, G_N_ELEMENTS(labels),
3482 sp_pencil_tb_tolerance_value_changed,
3483 1, 2);
3484 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3485 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3487 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3488 "tools.freehand.pencil");
3489 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3490 g_object_set_data(G_OBJECT(holder), "repr", repr);
3492 }
3494 /* advanced shape options */
3495 sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3497 /* Reset */
3498 {
3499 InkAction* inky = ink_action_new( "PencilResetAction",
3500 _("Defaults"),
3501 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3502 GTK_STOCK_CLEAR,
3503 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3504 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3505 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3506 }
3508 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3510 }
3513 //########################
3514 //## Tweak ##
3515 //########################
3517 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3518 {
3519 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3520 }
3522 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3523 {
3524 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3525 }
3527 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3528 {
3529 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3530 }
3532 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3533 {
3534 int mode = ege_select_one_action_get_active( act );
3535 prefs_set_int_attribute("tools.tweak", "mode", mode);
3537 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3538 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3539 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3540 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3541 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3542 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3543 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3544 if (doh) gtk_action_set_sensitive (doh, TRUE);
3545 if (dos) gtk_action_set_sensitive (dos, TRUE);
3546 if (dol) gtk_action_set_sensitive (dol, TRUE);
3547 if (doo) gtk_action_set_sensitive (doo, TRUE);
3548 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3549 if (fid) gtk_action_set_sensitive (fid, FALSE);
3550 } else {
3551 if (doh) gtk_action_set_sensitive (doh, FALSE);
3552 if (dos) gtk_action_set_sensitive (dos, FALSE);
3553 if (dol) gtk_action_set_sensitive (dol, FALSE);
3554 if (doo) gtk_action_set_sensitive (doo, FALSE);
3555 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3556 if (fid) gtk_action_set_sensitive (fid, TRUE);
3557 }
3558 }
3560 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3561 {
3562 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3563 }
3565 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3566 bool show = gtk_toggle_action_get_active( act );
3567 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3568 }
3569 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3570 bool show = gtk_toggle_action_get_active( act );
3571 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3572 }
3573 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3574 bool show = gtk_toggle_action_get_active( act );
3575 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3576 }
3577 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3578 bool show = gtk_toggle_action_get_active( act );
3579 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3580 }
3582 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3583 {
3584 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3586 {
3587 /* Width */
3588 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3589 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3590 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3591 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3592 "tools.tweak", "width", 15,
3593 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3594 1, 100, 1.0, 0.0,
3595 labels, values, G_N_ELEMENTS(labels),
3596 sp_tweak_width_value_changed, 0.01, 0, 100 );
3597 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3598 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3599 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3600 }
3603 {
3604 /* Force */
3605 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3606 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3607 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3608 _("Force"), _("Force:"), _("The force of the tweak action"),
3609 "tools.tweak", "force", 20,
3610 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3611 1, 100, 1.0, 0.0,
3612 labels, values, G_N_ELEMENTS(labels),
3613 sp_tweak_force_value_changed, 0.01, 0, 100 );
3614 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3615 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3616 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3617 }
3619 /* Mode */
3620 {
3621 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3623 GtkTreeIter iter;
3624 gtk_list_store_append( model, &iter );
3625 gtk_list_store_set( model, &iter,
3626 0, _("Push mode"),
3627 1, _("Push parts of paths in any direction"),
3628 2, "tweak_push_mode",
3629 -1 );
3631 gtk_list_store_append( model, &iter );
3632 gtk_list_store_set( model, &iter,
3633 0, _("Shrink mode"),
3634 1, _("Shrink (inset) parts of paths"),
3635 2, "tweak_shrink_mode",
3636 -1 );
3638 gtk_list_store_append( model, &iter );
3639 gtk_list_store_set( model, &iter,
3640 0, _("Grow mode"),
3641 1, _("Grow (outset) parts of paths"),
3642 2, "tweak_grow_mode",
3643 -1 );
3645 gtk_list_store_append( model, &iter );
3646 gtk_list_store_set( model, &iter,
3647 0, _("Attract mode"),
3648 1, _("Attract parts of paths towards cursor"),
3649 2, "tweak_attract_mode",
3650 -1 );
3652 gtk_list_store_append( model, &iter );
3653 gtk_list_store_set( model, &iter,
3654 0, _("Repel mode"),
3655 1, _("Repel parts of paths from cursor"),
3656 2, "tweak_repel_mode",
3657 -1 );
3659 gtk_list_store_append( model, &iter );
3660 gtk_list_store_set( model, &iter,
3661 0, _("Roughen mode"),
3662 1, _("Roughen parts of paths"),
3663 2, "tweak_roughen_mode",
3664 -1 );
3666 gtk_list_store_append( model, &iter );
3667 gtk_list_store_set( model, &iter,
3668 0, _("Color paint mode"),
3669 1, _("Paint the tool's color upon selected objects"),
3670 2, "tweak_colorpaint_mode",
3671 -1 );
3673 gtk_list_store_append( model, &iter );
3674 gtk_list_store_set( model, &iter,
3675 0, _("Color jitter mode"),
3676 1, _("Jitter the colors of selected objects"),
3677 2, "tweak_colorjitter_mode",
3678 -1 );
3680 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3681 g_object_set( act, "short_label", _("Mode:"), NULL );
3682 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3683 g_object_set_data( holder, "mode_action", act );
3685 ege_select_one_action_set_appearance( act, "full" );
3686 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3687 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3688 ege_select_one_action_set_icon_column( act, 2 );
3689 ege_select_one_action_set_icon_size( act, secondarySize );
3690 ege_select_one_action_set_tooltip_column( act, 1 );
3692 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3693 ege_select_one_action_set_active( act, mode );
3694 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3696 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3697 }
3699 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3701 {
3702 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3703 ege_output_action_set_use_markup( act, TRUE );
3704 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3705 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3706 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3707 g_object_set_data( holder, "tweak_channels_label", act);
3708 }
3710 {
3711 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3712 _("Hue"),
3713 _("In color mode, act on objects' hue"),
3714 NULL,
3715 Inkscape::ICON_SIZE_DECORATION );
3716 //TRANSLATORS: "H" here stands for hue
3717 g_object_set( act, "short_label", _("H"), NULL );
3718 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3719 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3720 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3721 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3722 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3723 g_object_set_data( holder, "tweak_doh", act);
3724 }
3725 {
3726 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3727 _("Saturation"),
3728 _("In color mode, act on objects' saturation"),
3729 NULL,
3730 Inkscape::ICON_SIZE_DECORATION );
3731 //TRANSLATORS: "S" here stands for Saturation
3732 g_object_set( act, "short_label", _("S"), NULL );
3733 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3734 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3735 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3736 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3737 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3738 g_object_set_data( holder, "tweak_dos", act );
3739 }
3740 {
3741 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3742 _("Lightness"),
3743 _("In color mode, act on objects' lightness"),
3744 NULL,
3745 Inkscape::ICON_SIZE_DECORATION );
3746 //TRANSLATORS: "L" here stands for Lightness
3747 g_object_set( act, "short_label", _("L"), NULL );
3748 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3749 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3750 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3751 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3752 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3753 g_object_set_data( holder, "tweak_dol", act );
3754 }
3755 {
3756 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3757 _("Opacity"),
3758 _("In color mode, act on objects' opacity"),
3759 NULL,
3760 Inkscape::ICON_SIZE_DECORATION );
3761 //TRANSLATORS: "O" here stands for Opacity
3762 g_object_set( act, "short_label", _("O"), NULL );
3763 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3764 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3765 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3766 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3767 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3768 g_object_set_data( holder, "tweak_doo", act );
3769 }
3771 { /* Fidelity */
3772 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3773 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3774 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3775 _("Fidelity"), _("Fidelity:"),
3776 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3777 "tools.tweak", "fidelity", 50,
3778 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3779 1, 100, 1.0, 10.0,
3780 labels, values, G_N_ELEMENTS(labels),
3781 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3782 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3783 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3784 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3785 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3786 g_object_set_data( holder, "tweak_fidelity", eact );
3787 }
3790 /* Use Pressure button */
3791 {
3792 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3793 _("Pressure"),
3794 _("Use the pressure of the input device to alter the force of tweak action"),
3795 "use_pressure",
3796 Inkscape::ICON_SIZE_DECORATION );
3797 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3798 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3799 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3800 }
3802 }
3805 //########################
3806 //## Calligraphy ##
3807 //########################
3808 static void update_presets_list(GObject *dataKludge ){
3810 if (g_object_get_data(dataKludge, "presets_blocked"))
3811 return;
3813 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3814 if (sel) {
3815 ege_select_one_action_set_active(sel, 0 );
3816 }
3817 }
3819 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3820 {
3821 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3822 update_presets_list(tbl);
3823 }
3825 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3826 {
3827 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3828 update_presets_list(tbl);
3829 }
3831 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3832 {
3833 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3834 update_presets_list(tbl);
3835 }
3837 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3838 {
3839 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3840 update_presets_list(tbl);
3841 }
3843 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3844 {
3845 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3846 update_presets_list(tbl);
3847 }
3849 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3850 {
3851 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3852 update_presets_list(tbl);
3853 }
3855 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3856 {
3857 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3858 update_presets_list(tbl);
3859 }
3861 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3862 {
3863 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3864 update_presets_list(tbl);
3865 }
3867 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3868 {
3869 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3870 update_presets_list(tbl);
3871 }
3873 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3874 {
3875 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3876 update_presets_list(tbl);
3877 }
3879 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3880 {
3881 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3882 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3883 update_presets_list(tbl);
3884 if (calligraphy_angle )
3885 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3886 }
3889 #define PROFILE_FLOAT_SIZE 7
3890 #define PROFILE_INT_SIZE 4
3891 struct ProfileFloatElement {
3892 char const *name;
3893 double def;
3894 double min;
3895 double max;
3896 };
3897 struct ProfileIntElement {
3898 char const *name;
3899 int def;
3900 int min;
3901 int max;
3902 };
3906 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3907 {"mass",2, 0.0, 100},
3908 {"wiggle",0.0, 0.0, 100},
3909 {"angle",30.0, -90.0, 90.0},
3910 {"thinning",10, -100, 100},
3911 {"tremor",0.0, 0.0, 100},
3912 {"flatness",90, 0.0, 100},
3913 {"cap_rounding",0.0, 0.0, 5.0}
3914 };
3915 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3916 {"width",15, 1, 100},
3917 {"usepressure",1,0,1},
3918 {"tracebackground",0,0,1},
3919 {"usetilt",1,0,1},
3920 };
3924 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3925 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3926 if (! desktop) return;
3928 if (g_object_get_data(dataKludge, "presets_blocked"))
3929 return;
3931 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3932 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3933 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3935 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3936 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3937 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3939 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3940 ProfileFloatElement const &pe = f_profile[i];
3941 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3942 prefs_set_double_attribute(pref_path,pe.name,v);
3943 }
3944 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3945 ProfileIntElement const &pe = i_profile[i];
3946 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3947 prefs_set_int_attribute(pref_path,pe.name,v);
3948 }
3949 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3951 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3952 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3953 GtkTreeIter iter;
3954 gtk_list_store_append( model, &iter );
3955 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3957 free(profile_id);
3958 free(pref_path);
3960 ege_select_one_action_set_active(selector, new_index);
3961 }
3964 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
3966 gint preset_index = ege_select_one_action_get_active( act );
3967 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3969 if (preset_path) {
3970 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
3972 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3974 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3975 iter;
3976 ++iter ) {
3977 const gchar *attr_name = g_quark_to_string(iter->key);
3978 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3979 continue;
3980 void *widget = g_object_get_data(tbl, attr_name);
3981 if (widget) {
3982 if (GTK_IS_ADJUSTMENT(widget)) {
3983 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3984 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3985 gtk_adjustment_set_value(adj, v);
3986 //std::cout << "set adj " << attr_name << " to " << v << "\n";
3987 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3988 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3989 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3990 gtk_toggle_action_set_active(toggle, v);
3991 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
3992 } else {
3993 g_warning("Unknown widget type for preset: %s\n", attr_name);
3994 }
3995 } else {
3996 g_warning("Bad key found in preset: %s\n", attr_name);
3997 }
3998 }
3999 free(preset_path);
4000 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4001 }
4003 }
4006 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4007 {
4008 {
4009 EgeAdjustmentAction* calligraphy_angle = 0;
4011 {
4012 /* Width */
4013 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4014 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4015 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4016 _("Pen Width"), _("Width:"),
4017 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4018 "tools.calligraphic", "width", 15,
4019 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4020 1, 100, 1.0, 0.0,
4021 labels, values, G_N_ELEMENTS(labels),
4022 sp_ddc_width_value_changed, 0.01, 0, 100 );
4023 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4024 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4025 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4026 }
4028 {
4029 /* Thinning */
4030 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4031 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4032 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4033 _("Stroke Thinning"), _("Thinning:"),
4034 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4035 "tools.calligraphic", "thinning", 10,
4036 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4037 -100, 100, 1, 0.1,
4038 labels, values, G_N_ELEMENTS(labels),
4039 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4040 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4041 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4042 }
4044 {
4045 /* Angle */
4046 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4047 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4048 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4049 _("Pen Angle"), _("Angle:"),
4050 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4051 "tools.calligraphic", "angle", 30,
4052 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4053 -90.0, 90.0, 1.0, 10.0,
4054 labels, values, G_N_ELEMENTS(labels),
4055 sp_ddc_angle_value_changed, 1, 0 );
4056 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4057 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4058 calligraphy_angle = eact;
4059 }
4061 {
4062 /* Fixation */
4063 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4064 gdouble values[] = {0, 20, 40, 60, 90, 100};
4065 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4066 _("Fixation"), _("Fixation:"),
4067 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4068 "tools.calligraphic", "flatness", 90,
4069 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4070 0.0, 100, 1.0, 10.0,
4071 labels, values, G_N_ELEMENTS(labels),
4072 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4073 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4074 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4075 }
4077 {
4078 /* Cap Rounding */
4079 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4080 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4081 // TRANSLATORS: "cap" means "end" (both start and finish) here
4082 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4083 _("Cap rounding"), _("Caps:"),
4084 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4085 "tools.calligraphic", "cap_rounding", 0.0,
4086 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4087 0.0, 5.0, 0.01, 0.1,
4088 labels, values, G_N_ELEMENTS(labels),
4089 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4090 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4091 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4092 }
4094 {
4095 /* Tremor */
4096 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4097 gdouble values[] = {0, 10, 20, 40, 60, 100};
4098 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4099 _("Stroke Tremor"), _("Tremor:"),
4100 _("Increase to make strokes rugged and trembling"),
4101 "tools.calligraphic", "tremor", 0.0,
4102 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4103 0.0, 100, 1, 0.0,
4104 labels, values, G_N_ELEMENTS(labels),
4105 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4107 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4108 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4109 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4110 }
4112 {
4113 /* Wiggle */
4114 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4115 gdouble values[] = {0, 20, 40, 60, 100};
4116 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4117 _("Pen Wiggle"), _("Wiggle:"),
4118 _("Increase to make the pen waver and wiggle"),
4119 "tools.calligraphic", "wiggle", 0.0,
4120 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4121 0.0, 100, 1, 0.0,
4122 labels, values, G_N_ELEMENTS(labels),
4123 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4124 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4125 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4126 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4127 }
4129 {
4130 /* Mass */
4131 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4132 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4133 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4134 _("Pen Mass"), _("Mass:"),
4135 _("Increase to make the pen drag behind, as if slowed by inertia"),
4136 "tools.calligraphic", "mass", 2.0,
4137 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4138 0.0, 100, 1, 0.0,
4139 labels, values, G_N_ELEMENTS(labels),
4140 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4141 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4142 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4143 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4144 }
4147 /* Trace Background button */
4148 {
4149 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4150 _("Trace Background"),
4151 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4152 "trace_background",
4153 Inkscape::ICON_SIZE_DECORATION );
4154 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4155 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4156 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4157 g_object_set_data( holder, "tracebackground", act );
4158 }
4160 /* Use Pressure button */
4161 {
4162 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4163 _("Pressure"),
4164 _("Use the pressure of the input device to alter the width of the pen"),
4165 "use_pressure",
4166 Inkscape::ICON_SIZE_DECORATION );
4167 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4168 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4169 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4170 g_object_set_data( holder, "usepressure", act );
4171 }
4173 /* Use Tilt button */
4174 {
4175 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4176 _("Tilt"),
4177 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4178 "use_tilt",
4179 Inkscape::ICON_SIZE_DECORATION );
4180 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4181 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4182 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4183 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4184 g_object_set_data( holder, "usetilt", act );
4185 }
4187 /*calligraphic profile */
4188 {
4189 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4190 gchar *pref_path;
4193 GtkTreeIter iter;
4194 gtk_list_store_append( model, &iter );
4195 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4197 //TODO: switch back to prefs API
4198 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4199 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4200 int ii=1;
4201 while (child_repr) {
4202 GtkTreeIter iter;
4203 char *preset_name = (char *) child_repr->attribute("name");
4204 gtk_list_store_append( model, &iter );
4205 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4206 child_repr = sp_repr_next(child_repr);
4207 }
4209 pref_path = NULL;
4210 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4211 ege_select_one_action_set_appearance( act1, "compact" );
4212 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4213 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4214 g_object_set_data( holder, "profile_selector", act1 );
4216 }
4218 /*Save or delete calligraphic profile */
4219 {
4220 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4221 _("Defaults"),
4222 _("Save current settings as new profile"),
4223 GTK_STOCK_SAVE );
4224 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4227 gtk_action_group_add_action( mainActions, act );
4228 gtk_action_set_sensitive( act, TRUE );
4229 g_object_set_data( holder, "profile_save_delete", act );
4230 }
4231 }
4232 }
4235 //########################
4236 //## Circle / Arc ##
4237 //########################
4239 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4240 {
4241 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4242 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4244 if (v1 == 0 && v2 == 0) {
4245 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4246 gtk_action_set_sensitive( ocb, FALSE );
4247 gtk_action_set_sensitive( make_whole, FALSE );
4248 }
4249 } else {
4250 gtk_action_set_sensitive( ocb, TRUE );
4251 gtk_action_set_sensitive( make_whole, TRUE );
4252 }
4253 }
4255 static void
4256 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4257 {
4258 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4260 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4261 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4262 }
4264 // quit if run by the attr_changed listener
4265 if (g_object_get_data( tbl, "freeze" )) {
4266 return;
4267 }
4269 // in turn, prevent listener from responding
4270 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4272 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4274 bool modmade = false;
4275 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4276 items != NULL;
4277 items = items->next)
4278 {
4279 SPItem *item = SP_ITEM(items->data);
4281 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4283 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4284 SPArc *arc = SP_ARC(item);
4286 if (!strcmp(value_name, "start"))
4287 ge->start = (adj->value * M_PI)/ 180;
4288 else
4289 ge->end = (adj->value * M_PI)/ 180;
4291 sp_genericellipse_normalize(ge);
4292 ((SPObject *)arc)->updateRepr();
4293 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4295 modmade = true;
4296 }
4297 }
4299 g_free(namespaced_name);
4301 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4303 sp_arctb_sensitivize( tbl, adj->value, other->value );
4305 if (modmade) {
4306 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4307 _("Arc: Change start/end"));
4308 }
4310 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4311 }
4314 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4315 {
4316 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4317 }
4319 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4320 {
4321 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4322 }
4325 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4326 {
4327 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4328 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4329 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4330 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4331 }
4333 // only take action if run by the attr_changed listener
4334 if (!g_object_get_data( tbl, "freeze" )) {
4335 // in turn, prevent listener from responding
4336 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4338 if ( eraserMode != 0 ) {
4339 } else {
4340 }
4341 // TODO finish implementation
4343 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4344 }
4345 }
4347 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4348 {
4349 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4350 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4351 if ( ege_select_one_action_get_active( act ) != 0 ) {
4352 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4353 } else {
4354 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4355 }
4356 }
4358 // quit if run by the attr_changed listener
4359 if (g_object_get_data( tbl, "freeze" )) {
4360 return;
4361 }
4363 // in turn, prevent listener from responding
4364 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4366 bool modmade = false;
4368 if ( ege_select_one_action_get_active(act) != 0 ) {
4369 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4370 items != NULL;
4371 items = items->next)
4372 {
4373 if (SP_IS_ARC((SPItem *) items->data)) {
4374 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4375 repr->setAttribute("sodipodi:open", "true");
4376 SP_OBJECT((SPItem *) items->data)->updateRepr();
4377 modmade = true;
4378 }
4379 }
4380 } else {
4381 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4382 items != NULL;
4383 items = items->next)
4384 {
4385 if (SP_IS_ARC((SPItem *) items->data)) {
4386 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4387 repr->setAttribute("sodipodi:open", NULL);
4388 SP_OBJECT((SPItem *) items->data)->updateRepr();
4389 modmade = true;
4390 }
4391 }
4392 }
4394 if (modmade) {
4395 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4396 _("Arc: Change open/closed"));
4397 }
4399 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4400 }
4402 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4403 {
4404 GtkAdjustment *adj;
4405 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4406 gtk_adjustment_set_value(adj, 0.0);
4407 gtk_adjustment_value_changed(adj);
4409 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4410 gtk_adjustment_set_value(adj, 0.0);
4411 gtk_adjustment_value_changed(adj);
4413 spinbutton_defocus( GTK_OBJECT(obj) );
4414 }
4416 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4417 gchar const */*old_value*/, gchar const */*new_value*/,
4418 bool /*is_interactive*/, gpointer data)
4419 {
4420 GObject *tbl = G_OBJECT(data);
4422 // quit if run by the _changed callbacks
4423 if (g_object_get_data( tbl, "freeze" )) {
4424 return;
4425 }
4427 // in turn, prevent callbacks from responding
4428 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4430 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4431 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4433 GtkAdjustment *adj1,*adj2;
4434 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4435 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4436 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4437 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4439 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4441 char const *openstr = NULL;
4442 openstr = repr->attribute("sodipodi:open");
4443 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4445 if (openstr) {
4446 ege_select_one_action_set_active( ocb, 1 );
4447 } else {
4448 ege_select_one_action_set_active( ocb, 0 );
4449 }
4451 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4452 }
4454 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4455 NULL, /* child_added */
4456 NULL, /* child_removed */
4457 arc_tb_event_attr_changed,
4458 NULL, /* content_changed */
4459 NULL /* order_changed */
4460 };
4463 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4464 {
4465 int n_selected = 0;
4466 Inkscape::XML::Node *repr = NULL;
4468 purge_repr_listener( tbl, tbl );
4470 for (GSList const *items = selection->itemList();
4471 items != NULL;
4472 items = items->next)
4473 {
4474 if (SP_IS_ARC((SPItem *) items->data)) {
4475 n_selected++;
4476 repr = SP_OBJECT_REPR((SPItem *) items->data);
4477 }
4478 }
4480 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4482 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4483 if (n_selected == 0) {
4484 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4485 } else if (n_selected == 1) {
4486 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4487 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4489 if (repr) {
4490 g_object_set_data( tbl, "repr", repr );
4491 Inkscape::GC::anchor(repr);
4492 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4493 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4494 }
4495 } else {
4496 // FIXME: implement averaging of all parameters for multiple selected
4497 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4498 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4499 sp_arctb_sensitivize( tbl, 1, 0 );
4500 }
4501 }
4504 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4505 {
4506 EgeAdjustmentAction* eact = 0;
4507 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4510 {
4511 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4512 ege_output_action_set_use_markup( act, TRUE );
4513 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4514 g_object_set_data( holder, "mode_action", act );
4515 }
4517 /* Start */
4518 {
4519 eact = create_adjustment_action( "ArcStartAction",
4520 _("Start"), _("Start:"),
4521 _("The angle (in degrees) from the horizontal to the arc's start point"),
4522 "tools.shapes.arc", "start", 0.0,
4523 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4524 -360.0, 360.0, 1.0, 10.0,
4525 0, 0, 0,
4526 sp_arctb_start_value_changed);
4527 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4528 }
4530 /* End */
4531 {
4532 eact = create_adjustment_action( "ArcEndAction",
4533 _("End"), _("End:"),
4534 _("The angle (in degrees) from the horizontal to the arc's end point"),
4535 "tools.shapes.arc", "end", 0.0,
4536 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4537 -360.0, 360.0, 1.0, 10.0,
4538 0, 0, 0,
4539 sp_arctb_end_value_changed);
4540 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4541 }
4543 /* Segments / Pie checkbox */
4544 {
4545 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4547 GtkTreeIter iter;
4548 gtk_list_store_append( model, &iter );
4549 gtk_list_store_set( model, &iter,
4550 0, _("Closed arc"),
4551 1, _("Switch to segment (closed shape with two radii)"),
4552 2, "circle_closed_arc",
4553 -1 );
4555 gtk_list_store_append( model, &iter );
4556 gtk_list_store_set( model, &iter,
4557 0, _("Open Arc"),
4558 1, _("Switch to arc (unclosed shape)"),
4559 2, "circle_open_arc",
4560 -1 );
4562 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4563 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4564 g_object_set_data( holder, "open_action", act );
4566 ege_select_one_action_set_appearance( act, "full" );
4567 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4568 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4569 ege_select_one_action_set_icon_column( act, 2 );
4570 ege_select_one_action_set_icon_size( act, secondarySize );
4571 ege_select_one_action_set_tooltip_column( act, 1 );
4573 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4574 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4575 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4576 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4577 }
4579 /* Make Whole */
4580 {
4581 InkAction* inky = ink_action_new( "ArcResetAction",
4582 _("Make whole"),
4583 _("Make the shape a whole ellipse, not arc or segment"),
4584 "reset_circle",
4585 secondarySize );
4586 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4587 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4588 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4589 g_object_set_data( holder, "make_whole", inky );
4590 }
4592 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4593 // sensitivize make whole and open checkbox
4594 {
4595 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4596 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4597 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4598 }
4601 sigc::connection *connection = new sigc::connection(
4602 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4603 );
4604 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4605 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4606 }
4611 // toggle button callbacks and updaters
4613 //########################
4614 //## Dropper ##
4615 //########################
4617 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4618 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4619 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4620 if ( set_action ) {
4621 if ( gtk_toggle_action_get_active( act ) ) {
4622 gtk_action_set_sensitive( set_action, TRUE );
4623 } else {
4624 gtk_action_set_sensitive( set_action, FALSE );
4625 }
4626 }
4628 spinbutton_defocus(GTK_OBJECT(tbl));
4629 }
4631 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4632 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4633 spinbutton_defocus(GTK_OBJECT(tbl));
4634 }
4637 /**
4638 * Dropper auxiliary toolbar construction and setup.
4639 *
4640 * TODO: Would like to add swatch of current color.
4641 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4642 * can drag and drop places. Will provide a nice mixing palette.
4643 */
4644 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4645 {
4646 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4648 {
4649 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4650 ege_output_action_set_use_markup( act, TRUE );
4651 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4652 }
4654 {
4655 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4656 _("Pick opacity"),
4657 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4658 NULL,
4659 Inkscape::ICON_SIZE_DECORATION );
4660 g_object_set( act, "short_label", _("Pick"), NULL );
4661 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4662 g_object_set_data( holder, "pick_action", act );
4663 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4664 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4665 }
4667 {
4668 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4669 _("Assign opacity"),
4670 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4671 NULL,
4672 Inkscape::ICON_SIZE_DECORATION );
4673 g_object_set( act, "short_label", _("Assign"), NULL );
4674 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4675 g_object_set_data( holder, "set_action", act );
4676 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4677 // make sure it's disabled if we're not picking alpha
4678 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4679 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4680 }
4681 }
4685 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4686 {
4687 {
4688 /* Width */
4689 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4690 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4691 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4692 _("Pen Width"), _("Width:"),
4693 _("The width of the eraser pen (relative to the visible canvas area)"),
4694 "tools.eraser", "width", 15,
4695 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4696 1, 100, 1.0, 0.0,
4697 labels, values, G_N_ELEMENTS(labels),
4698 sp_ddc_width_value_changed, 0.01, 0, 100 );
4699 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4700 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4701 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4702 }
4704 {
4705 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4707 GtkTreeIter iter;
4708 gtk_list_store_append( model, &iter );
4709 gtk_list_store_set( model, &iter,
4710 0, _("Delete"),
4711 1, _("Delete objects touched by the eraser"),
4712 2, "delete_object",
4713 -1 );
4715 gtk_list_store_append( model, &iter );
4716 gtk_list_store_set( model, &iter,
4717 0, _("Cut"),
4718 1, _("Cut out from objects"),
4719 2, "difference",
4720 -1 );
4722 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4723 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4724 g_object_set_data( holder, "eraser_mode_action", act );
4726 ege_select_one_action_set_appearance( act, "full" );
4727 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4728 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4729 ege_select_one_action_set_icon_column( act, 2 );
4730 ege_select_one_action_set_tooltip_column( act, 1 );
4732 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4733 ege_select_one_action_set_active( act, eraserMode );
4734 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4735 }
4737 }
4739 //########################
4740 //## Text Toolbox ##
4741 //########################
4742 /*
4743 static void
4744 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4745 {
4746 //Call back for letter sizing spinbutton
4747 }
4749 static void
4750 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4751 {
4752 //Call back for line height spinbutton
4753 }
4755 static void
4756 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4757 {
4758 //Call back for horizontal kerning spinbutton
4759 }
4761 static void
4762 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4763 {
4764 //Call back for vertical kerning spinbutton
4765 }
4767 static void
4768 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4769 {
4770 //Call back for letter rotation spinbutton
4771 }*/
4773 namespace {
4775 bool popdown_visible = false;
4776 bool popdown_hasfocus = false;
4778 void
4779 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4780 {
4781 SPStyle *query =
4782 sp_style_new (SP_ACTIVE_DOCUMENT);
4784 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4786 int result_family =
4787 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4789 int result_style =
4790 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4792 int result_numbers =
4793 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4795 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4797 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4798 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4799 {
4800 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4802 if (repr)
4803 {
4804 sp_style_read_from_repr (query, repr);
4805 }
4806 else
4807 {
4808 return;
4809 }
4810 }
4812 if (query->text)
4813 {
4814 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4815 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4816 gtk_entry_set_text (GTK_ENTRY (entry), "");
4818 } else if (query->text->font_specification.value || query->text->font_family.value) {
4820 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4822 // Get the font that corresponds
4823 Glib::ustring familyName;
4825 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4826 if (font) {
4827 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4828 font->Unref();
4829 font = NULL;
4830 }
4832 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4834 Gtk::TreePath path;
4835 try {
4836 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4837 } catch (...) {
4838 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4839 return;
4840 }
4842 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4843 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4845 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4847 gtk_tree_selection_select_path (tselection, path.gobj());
4848 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4850 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4851 }
4853 //Size
4854 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4855 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4856 g_object_set_data (tbl, "size-block", gpointer(1));
4857 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4858 g_object_set_data (tbl, "size-block", gpointer(0));
4859 free (str);
4861 //Anchor
4862 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4863 {
4864 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4865 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4866 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4867 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4868 }
4869 else
4870 {
4871 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4872 {
4873 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4874 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4875 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4876 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4877 }
4878 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4879 {
4880 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4881 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4882 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4883 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4884 }
4885 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4886 {
4887 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4888 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4889 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4890 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4891 }
4892 }
4894 //Style
4895 {
4896 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4898 gboolean active = gtk_toggle_button_get_active (button);
4899 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4901 if (active != check)
4902 {
4903 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4904 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4905 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4906 }
4907 }
4909 {
4910 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4912 gboolean active = gtk_toggle_button_get_active (button);
4913 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4915 if (active != check)
4916 {
4917 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4918 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4919 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4920 }
4921 }
4923 //Orientation
4924 //locking both buttons, changing one affect all group (both)
4925 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4926 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4928 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4929 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4931 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4932 {
4933 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4934 }
4935 else
4936 {
4937 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4938 }
4939 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4940 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4941 }
4943 sp_style_unref(query);
4944 }
4946 void
4947 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4948 {
4949 sp_text_toolbox_selection_changed (selection, tbl);
4950 }
4952 void
4953 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4954 {
4955 sp_text_toolbox_selection_changed (NULL, tbl);
4956 }
4958 void
4959 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4960 GObject *tbl)
4961 {
4962 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4963 GtkTreeModel *model = 0;
4964 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4965 GtkTreeIter iter;
4966 char *family = 0;
4968 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4969 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4971 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4972 return;
4973 }
4975 gtk_tree_model_get (model, &iter, 0, &family, -1);
4977 if (g_object_get_data (G_OBJECT (selection), "block"))
4978 {
4979 gtk_entry_set_text (GTK_ENTRY (entry), family);
4980 return;
4981 }
4983 gtk_entry_set_text (GTK_ENTRY (entry), family);
4985 SPStyle *query =
4986 sp_style_new (SP_ACTIVE_DOCUMENT);
4988 int result_fontspec =
4989 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4991 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4993 SPCSSAttr *css = sp_repr_css_attr_new ();
4996 // First try to get the font spec from the stored value
4997 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4999 if (fontSpec.empty()) {
5000 // Construct a new font specification if it does not yet exist
5001 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5002 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5003 fontFromStyle->Unref();
5004 }
5006 if (!fontSpec.empty()) {
5007 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5008 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5009 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5010 if (font) {
5011 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5013 // Set all the these just in case they were altered when finding the best
5014 // match for the new family and old style...
5016 gchar c[256];
5018 font->Family(c, 256);
5019 sp_repr_css_set_property (css, "font-family", c);
5021 font->Attribute( "weight", c, 256);
5022 sp_repr_css_set_property (css, "font-weight", c);
5024 font->Attribute("style", c, 256);
5025 sp_repr_css_set_property (css, "font-style", c);
5027 font->Attribute("stretch", c, 256);
5028 sp_repr_css_set_property (css, "font-stretch", c);
5030 font->Attribute("variant", c, 256);
5031 sp_repr_css_set_property (css, "font-variant", c);
5033 font->Unref();
5034 }
5035 }
5036 }
5038 // If querying returned nothing, set the default style of the tool (for new texts)
5039 if (result_fontspec == QUERY_STYLE_NOTHING)
5040 {
5041 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5042 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5043 }
5044 else
5045 {
5046 sp_desktop_set_style (desktop, css, true, true);
5047 }
5049 sp_style_unref(query);
5051 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5052 _("Text: Change font family"));
5053 sp_repr_css_attr_unref (css);
5054 free (family);
5055 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5057 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5058 }
5060 void
5061 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5062 GObject *tbl)
5063 {
5064 const char *family = gtk_entry_get_text (entry);
5066 try {
5067 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5068 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5069 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5070 gtk_tree_selection_select_path (selection, path.gobj());
5071 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5072 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5073 } catch (...) {
5074 if (family && strlen (family))
5075 {
5076 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5077 }
5078 }
5079 }
5081 void
5082 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5083 gpointer data)
5084 {
5085 if (g_object_get_data (G_OBJECT (button), "block")) return;
5086 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5087 int prop = GPOINTER_TO_INT(data);
5089 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5090 SPCSSAttr *css = sp_repr_css_attr_new ();
5092 switch (prop)
5093 {
5094 case 0:
5095 {
5096 sp_repr_css_set_property (css, "text-anchor", "start");
5097 sp_repr_css_set_property (css, "text-align", "start");
5098 break;
5099 }
5100 case 1:
5101 {
5102 sp_repr_css_set_property (css, "text-anchor", "middle");
5103 sp_repr_css_set_property (css, "text-align", "center");
5104 break;
5105 }
5107 case 2:
5108 {
5109 sp_repr_css_set_property (css, "text-anchor", "end");
5110 sp_repr_css_set_property (css, "text-align", "end");
5111 break;
5112 }
5114 case 3:
5115 {
5116 sp_repr_css_set_property (css, "text-anchor", "start");
5117 sp_repr_css_set_property (css, "text-align", "justify");
5118 break;
5119 }
5120 }
5122 SPStyle *query =
5123 sp_style_new (SP_ACTIVE_DOCUMENT);
5124 int result_numbers =
5125 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5127 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5128 if (result_numbers == QUERY_STYLE_NOTHING)
5129 {
5130 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5131 }
5133 sp_style_unref(query);
5135 sp_desktop_set_style (desktop, css, true, true);
5136 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5137 _("Text: Change alignment"));
5138 sp_repr_css_attr_unref (css);
5140 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5141 }
5143 void
5144 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5145 gpointer data)
5146 {
5147 if (g_object_get_data (G_OBJECT (button), "block")) return;
5149 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5150 SPCSSAttr *css = sp_repr_css_attr_new ();
5151 int prop = GPOINTER_TO_INT(data);
5152 bool active = gtk_toggle_button_get_active (button);
5154 SPStyle *query =
5155 sp_style_new (SP_ACTIVE_DOCUMENT);
5157 int result_fontspec =
5158 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5160 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5161 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5162 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5164 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5165 Glib::ustring newFontSpec = "";
5167 if (fontSpec.empty()) {
5168 // Construct a new font specification if it does not yet exist
5169 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5170 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5171 fontFromStyle->Unref();
5172 }
5174 switch (prop)
5175 {
5176 case 0:
5177 {
5178 if (!fontSpec.empty()) {
5179 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5180 }
5181 if (fontSpec != newFontSpec) {
5182 // Don't even set the bold if the font didn't exist on the system
5183 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5184 }
5185 break;
5186 }
5188 case 1:
5189 {
5190 if (!fontSpec.empty()) {
5191 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5192 }
5193 if (fontSpec != newFontSpec) {
5194 // Don't even set the italic if the font didn't exist on the system
5195 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5196 }
5197 break;
5198 }
5199 }
5201 if (!newFontSpec.empty()) {
5202 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5203 }
5205 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5206 if (result_fontspec == QUERY_STYLE_NOTHING)
5207 {
5208 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5209 }
5211 sp_style_unref(query);
5213 sp_desktop_set_style (desktop, css, true, true);
5214 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5215 _("Text: Change font style"));
5216 sp_repr_css_attr_unref (css);
5218 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5219 }
5221 void
5222 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5223 gpointer data)
5224 {
5225 if (g_object_get_data (G_OBJECT (button), "block")) {
5226 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5227 return;
5228 }
5230 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5231 SPCSSAttr *css = sp_repr_css_attr_new ();
5232 int prop = GPOINTER_TO_INT(data);
5234 switch (prop)
5235 {
5236 case 0:
5237 {
5238 sp_repr_css_set_property (css, "writing-mode", "lr");
5239 break;
5240 }
5242 case 1:
5243 {
5244 sp_repr_css_set_property (css, "writing-mode", "tb");
5245 break;
5246 }
5247 }
5249 SPStyle *query =
5250 sp_style_new (SP_ACTIVE_DOCUMENT);
5251 int result_numbers =
5252 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5254 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5255 if (result_numbers == QUERY_STYLE_NOTHING)
5256 {
5257 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5258 }
5260 sp_desktop_set_style (desktop, css, true, true);
5261 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5262 _("Text: Change orientation"));
5263 sp_repr_css_attr_unref (css);
5265 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5266 }
5268 gboolean
5269 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5270 {
5271 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5272 if (!desktop) return FALSE;
5274 switch (get_group0_keyval (event)) {
5275 case GDK_Escape: // defocus
5276 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5277 sp_text_toolbox_selection_changed (NULL, tbl); // update
5278 return TRUE; // I consumed the event
5279 break;
5280 }
5281 return FALSE;
5282 }
5284 gboolean
5285 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5286 {
5287 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5288 if (!desktop) return FALSE;
5290 switch (get_group0_keyval (event)) {
5291 case GDK_KP_Enter:
5292 case GDK_Return:
5293 case GDK_Escape: // defocus
5294 gtk_widget_hide (w);
5295 popdown_visible = false;
5296 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5297 return TRUE; // I consumed the event
5298 break;
5299 case GDK_w:
5300 case GDK_W:
5301 if (event->state & GDK_CONTROL_MASK) {
5302 gtk_widget_hide (w);
5303 popdown_visible = false;
5304 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5305 return TRUE; // I consumed the event
5306 }
5307 break;
5308 }
5309 return FALSE;
5310 }
5313 void
5314 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5315 GObject *tbl)
5316 {
5317 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5319 if (g_object_get_data (tbl, "size-block")) return;
5321 // If this is not from selecting a size in the list (in which case get_active will give the
5322 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5323 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5324 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5325 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5326 return;
5328 gchar *endptr;
5329 gdouble value = -1;
5330 char *text = gtk_combo_box_get_active_text (cbox);
5331 if (text) {
5332 value = g_strtod (text, &endptr);
5333 if (endptr == text) // conversion failed, non-numeric input
5334 value = -1;
5335 free (text);
5336 }
5337 if (value <= 0) {
5338 return; // could not parse value
5339 }
5341 SPCSSAttr *css = sp_repr_css_attr_new ();
5342 Inkscape::CSSOStringStream osfs;
5343 osfs << value;
5344 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5346 SPStyle *query =
5347 sp_style_new (SP_ACTIVE_DOCUMENT);
5348 int result_numbers =
5349 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5351 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5352 if (result_numbers == QUERY_STYLE_NOTHING)
5353 {
5354 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5355 }
5357 sp_style_unref(query);
5359 sp_desktop_set_style (desktop, css, true, true);
5360 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5361 _("Text: Change font size"));
5362 sp_repr_css_attr_unref (css);
5364 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5365 }
5367 gboolean
5368 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5369 {
5370 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5371 if (!desktop) return FALSE;
5373 if (!g_object_get_data (tbl, "esc-pressed")) {
5374 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5375 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5376 sp_text_toolbox_size_changed (cbox, tbl);
5377 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5378 }
5379 return FALSE; // I consumed the event
5380 }
5383 gboolean
5384 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5385 {
5386 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5387 if (!desktop) return FALSE;
5389 switch (get_group0_keyval (event)) {
5390 case GDK_Escape: // defocus
5391 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5392 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5393 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5394 return TRUE; // I consumed the event
5395 break;
5396 case GDK_Return: // defocus
5397 case GDK_KP_Enter:
5398 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5399 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5400 sp_text_toolbox_size_changed (cbox, tbl);
5401 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5402 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5403 return TRUE; // I consumed the event
5404 break;
5405 }
5406 return FALSE;
5407 }
5409 void
5410 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5411 GObject *tbl)
5412 {
5413 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5414 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5415 int x, y;
5417 if (!popdown_visible)
5418 {
5419 gdk_window_get_origin (widget->window, &x, &y);
5420 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5421 gtk_widget_show_all (popdown);
5422 //sp_transientize (popdown);
5424 gdk_pointer_grab (widget->window, TRUE,
5425 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5426 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5427 GDK_POINTER_MOTION_MASK),
5428 NULL, NULL, GDK_CURRENT_TIME);
5430 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5432 popdown_visible = true;
5433 }
5434 else
5435 {
5436 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5437 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5438 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5439 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5440 gtk_widget_hide (popdown);
5441 popdown_visible = false;
5442 }
5443 }
5445 gboolean
5446 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5447 GdkEventFocus */*event*/,
5448 GObject */*tbl*/)
5449 {
5450 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5451 return FALSE;
5452 }
5454 gboolean
5455 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5456 GdkEventFocus */*event*/,
5457 GObject */*tbl*/)
5458 {
5459 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5461 if (popdown_hasfocus) {
5462 gtk_widget_hide (popdown);
5463 popdown_hasfocus = false;
5464 popdown_visible = false;
5465 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5466 return TRUE;
5467 }
5468 return FALSE;
5469 }
5471 gboolean
5472 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5473 GdkEventFocus */*event*/,
5474 GObject */*tbl*/)
5475 {
5476 popdown_hasfocus = true;
5477 return TRUE;
5478 }
5481 void
5482 cell_data_func (GtkTreeViewColumn */*column*/,
5483 GtkCellRenderer *cell,
5484 GtkTreeModel *tree_model,
5485 GtkTreeIter *iter,
5486 gpointer /*data*/)
5487 {
5488 char *family,
5489 *family_escaped,
5490 *sample_escaped;
5492 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5494 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5496 family_escaped = g_markup_escape_text (family, -1);
5497 sample_escaped = g_markup_escape_text (sample, -1);
5499 std::stringstream markup;
5500 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5501 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5503 free (family);
5504 free (family_escaped);
5505 free (sample_escaped);
5506 }
5508 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5509 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5510 if (completion) {
5511 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5512 g_object_unref (completion);
5513 }
5514 }
5516 GtkWidget*
5517 sp_text_toolbox_new (SPDesktop *desktop)
5518 {
5519 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5520 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5522 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5523 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5525 GtkTooltips *tt = gtk_tooltips_new();
5526 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5528 ////////////Family
5529 //Window
5530 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5531 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5533 //Entry
5534 GtkWidget *entry = gtk_entry_new ();
5535 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5536 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5537 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5538 gtk_entry_completion_set_text_column (completion, 0);
5539 gtk_entry_completion_set_minimum_key_length (completion, 1);
5540 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5541 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5542 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5543 gtk_toolbar_append_widget( tbl, entry, "", "" );
5544 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5546 //Button
5547 GtkWidget *button = gtk_button_new ();
5548 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5549 gtk_toolbar_append_widget( tbl, button, "", "");
5551 //Popdown
5552 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5553 GtkWidget *treeview = gtk_tree_view_new ();
5555 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5556 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5557 gtk_tree_view_column_pack_start (column, cell, FALSE);
5558 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5559 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5560 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5562 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5563 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5564 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5566 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5568 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5569 gtk_container_add (GTK_CONTAINER (sw), treeview);
5571 gtk_container_add (GTK_CONTAINER (window), sw);
5572 gtk_widget_set_size_request (window, 300, 450);
5574 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5575 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5576 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5578 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5580 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5581 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5582 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5584 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5585 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5587 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5588 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5589 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5590 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5591 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5593 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5594 GtkWidget *box = gtk_event_box_new ();
5595 gtk_container_add (GTK_CONTAINER (box), image);
5596 gtk_toolbar_append_widget( tbl, box, "", "");
5597 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5598 GtkTooltips *tooltips = gtk_tooltips_new ();
5599 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5600 gtk_widget_hide (GTK_WIDGET (box));
5601 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5603 ////////////Size
5604 const char *sizes[] = {
5605 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5606 "16", "18", "20", "22", "24", "28",
5607 "32", "36", "40", "48", "56", "64", "72", "144"
5608 };
5610 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5611 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5612 gtk_widget_set_size_request (cbox, 80, -1);
5613 gtk_toolbar_append_widget( tbl, cbox, "", "");
5614 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5615 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5616 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5617 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5619 ////////////Text anchor
5620 GtkWidget *group = gtk_radio_button_new (NULL);
5621 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5622 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5624 // left
5625 GtkWidget *rbutton = group;
5626 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5627 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5628 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5630 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5631 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5632 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5633 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5635 // center
5636 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5637 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5638 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5639 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5641 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5642 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5643 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5644 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5646 // right
5647 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5648 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5649 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5650 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5652 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5653 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5654 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5655 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5657 // fill
5658 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5659 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5660 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5661 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5663 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5664 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5665 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5666 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5668 gtk_toolbar_append_widget( tbl, row, "", "");
5670 //spacer
5671 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5673 ////////////Text style
5674 row = gtk_hbox_new (FALSE, 4);
5676 // bold
5677 rbutton = gtk_toggle_button_new ();
5678 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5679 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5680 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5681 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5683 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5684 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5685 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5687 // italic
5688 rbutton = gtk_toggle_button_new ();
5689 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5690 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5691 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5692 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5694 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5695 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5696 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5698 gtk_toolbar_append_widget( tbl, row, "", "");
5700 //spacer
5701 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5703 ////////////Text orientation
5704 group = gtk_radio_button_new (NULL);
5705 row = gtk_hbox_new (FALSE, 4);
5706 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5708 // horizontal
5709 rbutton = group;
5710 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5711 gtk_container_add (GTK_CONTAINER (rbutton),
5712 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5713 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5714 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5716 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5717 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5718 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5720 // vertical
5721 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5722 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5723 gtk_container_add (GTK_CONTAINER (rbutton),
5724 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5725 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5726 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5728 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5729 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5730 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5731 gtk_toolbar_append_widget( tbl, row, "", "" );
5734 //watch selection
5735 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5737 sigc::connection *c_selection_changed =
5738 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5739 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5740 pool->add_connection ("selection-changed", c_selection_changed);
5742 sigc::connection *c_selection_modified =
5743 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5744 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5745 pool->add_connection ("selection-modified", c_selection_modified);
5747 sigc::connection *c_subselection_changed =
5748 new sigc::connection (desktop->connectToolSubselectionChanged
5749 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5750 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5752 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5755 gtk_widget_show_all( GTK_WIDGET(tbl) );
5757 return GTK_WIDGET(tbl);
5758 } // end of sp_text_toolbox_new()
5760 }//<unnamed> namespace
5763 //#########################
5764 //## Connector ##
5765 //#########################
5767 static void sp_connector_path_set_avoid(void)
5768 {
5769 cc_selection_set_avoid(true);
5770 }
5773 static void sp_connector_path_set_ignore(void)
5774 {
5775 cc_selection_set_avoid(false);
5776 }
5780 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5781 {
5782 // quit if run by the _changed callbacks
5783 if (g_object_get_data( tbl, "freeze" )) {
5784 return;
5785 }
5787 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5788 SPDocument *doc = sp_desktop_document(desktop);
5790 if (!sp_document_get_undo_sensitive(doc))
5791 {
5792 return;
5793 }
5795 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5797 if ( repr->attribute("inkscape:connector-spacing") ) {
5798 gdouble priorValue = gtk_adjustment_get_value(adj);
5799 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5800 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5801 return;
5802 }
5803 } else if ( adj->value == defaultConnSpacing ) {
5804 return;
5805 }
5807 // in turn, prevent callbacks from responding
5808 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5810 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5811 SP_OBJECT(desktop->namedview)->updateRepr();
5813 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5814 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5815 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5816 NR::Matrix m = NR::identity();
5817 avoid_item_move(&m, item);
5818 }
5820 if (items) {
5821 g_slist_free(items);
5822 }
5824 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5825 _("Change connector spacing"));
5827 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5829 spinbutton_defocus(GTK_OBJECT(tbl));
5830 }
5832 static void sp_connector_graph_layout(void)
5833 {
5834 if (!SP_ACTIVE_DESKTOP) return;
5836 // hack for clones, see comment in align-and-distribute.cpp
5837 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5838 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5840 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5842 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5844 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5845 }
5847 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5848 {
5849 if ( gtk_toggle_action_get_active( act ) ) {
5850 prefs_set_string_attribute("tools.connector", "directedlayout",
5851 "true");
5852 } else {
5853 prefs_set_string_attribute("tools.connector", "directedlayout",
5854 "false");
5855 }
5856 }
5858 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5859 {
5860 if ( gtk_toggle_action_get_active( act ) ) {
5861 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5862 "true");
5863 } else {
5864 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5865 "false");
5866 }
5867 }
5870 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5871 {
5872 prefs_set_double_attribute("tools.connector", "length", adj->value);
5873 spinbutton_defocus(GTK_OBJECT(tbl));
5874 }
5876 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5877 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5878 bool /*is_interactive*/, gpointer data)
5879 {
5880 GtkWidget *tbl = GTK_WIDGET(data);
5882 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5883 return;
5884 }
5885 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5886 return;
5887 }
5889 GtkAdjustment *adj = (GtkAdjustment*)
5890 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5891 gdouble spacing = defaultConnSpacing;
5892 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5894 gtk_adjustment_set_value(adj, spacing);
5895 }
5898 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5899 NULL, /* child_added */
5900 NULL, /* child_removed */
5901 connector_tb_event_attr_changed,
5902 NULL, /* content_changed */
5903 NULL /* order_changed */
5904 };
5907 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5908 {
5909 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5911 {
5912 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5913 _("Avoid"),
5914 _("Make connectors avoid selected objects"),
5915 "connector_avoid",
5916 secondarySize );
5917 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5918 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5919 }
5921 {
5922 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5923 _("Ignore"),
5924 _("Make connectors ignore selected objects"),
5925 "connector_ignore",
5926 secondarySize );
5927 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5928 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5929 }
5931 EgeAdjustmentAction* eact = 0;
5933 // Spacing spinbox
5934 eact = create_adjustment_action( "ConnectorSpacingAction",
5935 _("Connector Spacing"), _("Spacing:"),
5936 _("The amount of space left around objects by auto-routing connectors"),
5937 "tools.connector", "spacing", defaultConnSpacing,
5938 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5939 0, 100, 1.0, 10.0,
5940 0, 0, 0,
5941 connector_spacing_changed, 1, 0 );
5942 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5944 // Graph (connector network) layout
5945 {
5946 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5947 _("Graph"),
5948 _("Nicely arrange selected connector network"),
5949 "graph_layout",
5950 secondarySize );
5951 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5952 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5953 }
5955 // Default connector length spinbox
5956 eact = create_adjustment_action( "ConnectorLengthAction",
5957 _("Connector Length"), _("Length:"),
5958 _("Ideal length for connectors when layout is applied"),
5959 "tools.connector", "length", 100,
5960 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5961 10, 1000, 10.0, 100.0,
5962 0, 0, 0,
5963 connector_length_changed, 1, 0 );
5964 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5967 // Directed edges toggle button
5968 {
5969 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5970 _("Downwards"),
5971 _("Make connectors with end-markers (arrows) point downwards"),
5972 "directed_graph",
5973 Inkscape::ICON_SIZE_DECORATION );
5974 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5976 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5977 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5978 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5980 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5981 }
5983 // Avoid overlaps toggle button
5984 {
5985 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5986 _("Remove overlaps"),
5987 _("Do not allow overlapping shapes"),
5988 "remove_overlaps",
5989 Inkscape::ICON_SIZE_DECORATION );
5990 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5992 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5993 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5994 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5996 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5997 }
5999 // Code to watch for changes to the connector-spacing attribute in
6000 // the XML.
6001 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6002 g_assert(repr != NULL);
6004 purge_repr_listener( holder, holder );
6006 if (repr) {
6007 g_object_set_data( holder, "repr", repr );
6008 Inkscape::GC::anchor(repr);
6009 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6010 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6011 }
6012 } // end of sp_connector_toolbox_prep()
6015 //#########################
6016 //## Paintbucket ##
6017 //#########################
6019 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6020 {
6021 gint channels = ege_select_one_action_get_active( act );
6022 flood_channels_set_channels( channels );
6023 }
6025 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6026 {
6027 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6028 }
6030 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6031 {
6032 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6033 }
6035 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6036 {
6037 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6038 SPUnit const *unit = tracker->getActiveUnit();
6040 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6042 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6043 }
6045 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
6046 {
6047 // FIXME: make defaults settable via Inkscape Options
6048 struct KeyValue {
6049 char const *key;
6050 double value;
6051 } const key_values[] = {
6052 {"threshold", 15},
6053 {"offset", 0.0}
6054 };
6056 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6057 KeyValue const &kv = key_values[i];
6058 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
6059 if ( adj ) {
6060 gtk_adjustment_set_value(adj, kv.value);
6061 }
6062 }
6064 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
6065 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6066 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
6067 ege_select_one_action_set_active( autogap_action, 0 );
6068 }
6070 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6071 {
6072 EgeAdjustmentAction* eact = 0;
6074 {
6075 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6077 GList* items = 0;
6078 gint count = 0;
6079 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6080 {
6081 GtkTreeIter iter;
6082 gtk_list_store_append( model, &iter );
6083 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6084 count++;
6085 }
6086 g_list_free( items );
6087 items = 0;
6088 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6089 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6090 ege_select_one_action_set_appearance( act1, "compact" );
6091 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6092 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6093 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6094 g_object_set_data( holder, "channels_action", act1 );
6095 }
6097 // Spacing spinbox
6098 {
6099 eact = create_adjustment_action(
6100 "ThresholdAction",
6101 _("Fill Threshold"), _("Threshold:"),
6102 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6103 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6104 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6105 0, 0, 0,
6106 paintbucket_threshold_changed, 1, 0 );
6108 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6109 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6110 }
6112 // Create the units menu.
6113 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6114 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6115 if (stored_unit)
6116 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6117 g_object_set_data( holder, "tracker", tracker );
6118 {
6119 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6120 gtk_action_group_add_action( mainActions, act );
6121 }
6123 // Offset spinbox
6124 {
6125 eact = create_adjustment_action(
6126 "OffsetAction",
6127 _("Grow/shrink by"), _("Grow/shrink by:"),
6128 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6129 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6130 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6131 0, 0, 0,
6132 paintbucket_offset_changed, 1, 2);
6133 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6135 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6136 }
6138 /* Auto Gap */
6139 {
6140 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6142 GList* items = 0;
6143 gint count = 0;
6144 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6145 {
6146 GtkTreeIter iter;
6147 gtk_list_store_append( model, &iter );
6148 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6149 count++;
6150 }
6151 g_list_free( items );
6152 items = 0;
6153 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6154 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6155 ege_select_one_action_set_appearance( act2, "compact" );
6156 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6157 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6158 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6159 g_object_set_data( holder, "autogap_action", act2 );
6160 }
6162 /* Reset */
6163 {
6164 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6165 _("Defaults"),
6166 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6167 GTK_STOCK_CLEAR );
6168 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6169 gtk_action_group_add_action( mainActions, act );
6170 gtk_action_set_sensitive( act, TRUE );
6171 }
6173 }
6175 /*
6176 Local Variables:
6177 mode:c++
6178 c-file-style:"stroustrup"
6179 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6180 indent-tabs-mode:nil
6181 fill-column:99
6182 End:
6183 */
6184 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :