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_action"));
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 g_object_set_data( holder, "angle_action", eact );
4058 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4059 calligraphy_angle = eact;
4060 }
4062 {
4063 /* Fixation */
4064 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4065 gdouble values[] = {0, 20, 40, 60, 90, 100};
4066 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4067 _("Fixation"), _("Fixation:"),
4068 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4069 "tools.calligraphic", "flatness", 90,
4070 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4071 0.0, 100, 1.0, 10.0,
4072 labels, values, G_N_ELEMENTS(labels),
4073 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4074 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4075 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4076 }
4078 {
4079 /* Cap Rounding */
4080 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4081 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4082 // TRANSLATORS: "cap" means "end" (both start and finish) here
4083 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4084 _("Cap rounding"), _("Caps:"),
4085 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4086 "tools.calligraphic", "cap_rounding", 0.0,
4087 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4088 0.0, 5.0, 0.01, 0.1,
4089 labels, values, G_N_ELEMENTS(labels),
4090 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4091 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4092 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4093 }
4095 {
4096 /* Tremor */
4097 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4098 gdouble values[] = {0, 10, 20, 40, 60, 100};
4099 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4100 _("Stroke Tremor"), _("Tremor:"),
4101 _("Increase to make strokes rugged and trembling"),
4102 "tools.calligraphic", "tremor", 0.0,
4103 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4104 0.0, 100, 1, 0.0,
4105 labels, values, G_N_ELEMENTS(labels),
4106 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4108 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4109 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4110 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4111 }
4113 {
4114 /* Wiggle */
4115 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4116 gdouble values[] = {0, 20, 40, 60, 100};
4117 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4118 _("Pen Wiggle"), _("Wiggle:"),
4119 _("Increase to make the pen waver and wiggle"),
4120 "tools.calligraphic", "wiggle", 0.0,
4121 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4122 0.0, 100, 1, 0.0,
4123 labels, values, G_N_ELEMENTS(labels),
4124 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4125 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4126 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4127 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4128 }
4130 {
4131 /* Mass */
4132 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4133 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4134 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4135 _("Pen Mass"), _("Mass:"),
4136 _("Increase to make the pen drag behind, as if slowed by inertia"),
4137 "tools.calligraphic", "mass", 2.0,
4138 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4139 0.0, 100, 1, 0.0,
4140 labels, values, G_N_ELEMENTS(labels),
4141 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4142 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4143 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4144 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4145 }
4148 /* Trace Background button */
4149 {
4150 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4151 _("Trace Background"),
4152 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4153 "trace_background",
4154 Inkscape::ICON_SIZE_DECORATION );
4155 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4156 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4157 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4158 g_object_set_data( holder, "tracebackground", act );
4159 }
4161 /* Use Pressure button */
4162 {
4163 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4164 _("Pressure"),
4165 _("Use the pressure of the input device to alter the width of the pen"),
4166 "use_pressure",
4167 Inkscape::ICON_SIZE_DECORATION );
4168 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4169 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4170 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4171 g_object_set_data( holder, "usepressure", act );
4172 }
4174 /* Use Tilt button */
4175 {
4176 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4177 _("Tilt"),
4178 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4179 "use_tilt",
4180 Inkscape::ICON_SIZE_DECORATION );
4181 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4182 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4183 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4184 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4185 g_object_set_data( holder, "usetilt", act );
4186 }
4188 /*calligraphic profile */
4189 {
4190 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4191 gchar *pref_path;
4194 GtkTreeIter iter;
4195 gtk_list_store_append( model, &iter );
4196 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4198 //TODO: switch back to prefs API
4199 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4200 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4201 int ii=1;
4202 while (child_repr) {
4203 GtkTreeIter iter;
4204 char *preset_name = (char *) child_repr->attribute("name");
4205 gtk_list_store_append( model, &iter );
4206 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4207 child_repr = sp_repr_next(child_repr);
4208 }
4210 pref_path = NULL;
4211 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4212 ege_select_one_action_set_appearance( act1, "compact" );
4213 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4214 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4215 g_object_set_data( holder, "profile_selector", act1 );
4217 }
4219 /*Save or delete calligraphic profile */
4220 {
4221 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4222 _("Defaults"),
4223 _("Save current settings as new profile"),
4224 GTK_STOCK_SAVE );
4225 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4228 gtk_action_group_add_action( mainActions, act );
4229 gtk_action_set_sensitive( act, TRUE );
4230 g_object_set_data( holder, "profile_save_delete", act );
4231 }
4232 }
4233 }
4236 //########################
4237 //## Circle / Arc ##
4238 //########################
4240 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4241 {
4242 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4243 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4245 if (v1 == 0 && v2 == 0) {
4246 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4247 gtk_action_set_sensitive( ocb, FALSE );
4248 gtk_action_set_sensitive( make_whole, FALSE );
4249 }
4250 } else {
4251 gtk_action_set_sensitive( ocb, TRUE );
4252 gtk_action_set_sensitive( make_whole, TRUE );
4253 }
4254 }
4256 static void
4257 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4258 {
4259 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4261 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4262 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4263 }
4265 // quit if run by the attr_changed listener
4266 if (g_object_get_data( tbl, "freeze" )) {
4267 return;
4268 }
4270 // in turn, prevent listener from responding
4271 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4273 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4275 bool modmade = false;
4276 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4277 items != NULL;
4278 items = items->next)
4279 {
4280 SPItem *item = SP_ITEM(items->data);
4282 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4284 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4285 SPArc *arc = SP_ARC(item);
4287 if (!strcmp(value_name, "start"))
4288 ge->start = (adj->value * M_PI)/ 180;
4289 else
4290 ge->end = (adj->value * M_PI)/ 180;
4292 sp_genericellipse_normalize(ge);
4293 ((SPObject *)arc)->updateRepr();
4294 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4296 modmade = true;
4297 }
4298 }
4300 g_free(namespaced_name);
4302 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4304 sp_arctb_sensitivize( tbl, adj->value, other->value );
4306 if (modmade) {
4307 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4308 _("Arc: Change start/end"));
4309 }
4311 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4312 }
4315 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4316 {
4317 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4318 }
4320 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4321 {
4322 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4323 }
4326 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4327 {
4328 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4329 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4330 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4331 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4332 }
4334 // only take action if run by the attr_changed listener
4335 if (!g_object_get_data( tbl, "freeze" )) {
4336 // in turn, prevent listener from responding
4337 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4339 if ( eraserMode != 0 ) {
4340 } else {
4341 }
4342 // TODO finish implementation
4344 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4345 }
4346 }
4348 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4349 {
4350 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4351 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4352 if ( ege_select_one_action_get_active( act ) != 0 ) {
4353 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4354 } else {
4355 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4356 }
4357 }
4359 // quit if run by the attr_changed listener
4360 if (g_object_get_data( tbl, "freeze" )) {
4361 return;
4362 }
4364 // in turn, prevent listener from responding
4365 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4367 bool modmade = false;
4369 if ( ege_select_one_action_get_active(act) != 0 ) {
4370 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4371 items != NULL;
4372 items = items->next)
4373 {
4374 if (SP_IS_ARC((SPItem *) items->data)) {
4375 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4376 repr->setAttribute("sodipodi:open", "true");
4377 SP_OBJECT((SPItem *) items->data)->updateRepr();
4378 modmade = true;
4379 }
4380 }
4381 } else {
4382 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4383 items != NULL;
4384 items = items->next)
4385 {
4386 if (SP_IS_ARC((SPItem *) items->data)) {
4387 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4388 repr->setAttribute("sodipodi:open", NULL);
4389 SP_OBJECT((SPItem *) items->data)->updateRepr();
4390 modmade = true;
4391 }
4392 }
4393 }
4395 if (modmade) {
4396 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4397 _("Arc: Change open/closed"));
4398 }
4400 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4401 }
4403 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4404 {
4405 GtkAdjustment *adj;
4406 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4407 gtk_adjustment_set_value(adj, 0.0);
4408 gtk_adjustment_value_changed(adj);
4410 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4411 gtk_adjustment_set_value(adj, 0.0);
4412 gtk_adjustment_value_changed(adj);
4414 spinbutton_defocus( GTK_OBJECT(obj) );
4415 }
4417 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4418 gchar const */*old_value*/, gchar const */*new_value*/,
4419 bool /*is_interactive*/, gpointer data)
4420 {
4421 GObject *tbl = G_OBJECT(data);
4423 // quit if run by the _changed callbacks
4424 if (g_object_get_data( tbl, "freeze" )) {
4425 return;
4426 }
4428 // in turn, prevent callbacks from responding
4429 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4431 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4432 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4434 GtkAdjustment *adj1,*adj2;
4435 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4436 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4437 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4438 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4440 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4442 char const *openstr = NULL;
4443 openstr = repr->attribute("sodipodi:open");
4444 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4446 if (openstr) {
4447 ege_select_one_action_set_active( ocb, 1 );
4448 } else {
4449 ege_select_one_action_set_active( ocb, 0 );
4450 }
4452 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4453 }
4455 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4456 NULL, /* child_added */
4457 NULL, /* child_removed */
4458 arc_tb_event_attr_changed,
4459 NULL, /* content_changed */
4460 NULL /* order_changed */
4461 };
4464 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4465 {
4466 int n_selected = 0;
4467 Inkscape::XML::Node *repr = NULL;
4469 purge_repr_listener( tbl, tbl );
4471 for (GSList const *items = selection->itemList();
4472 items != NULL;
4473 items = items->next)
4474 {
4475 if (SP_IS_ARC((SPItem *) items->data)) {
4476 n_selected++;
4477 repr = SP_OBJECT_REPR((SPItem *) items->data);
4478 }
4479 }
4481 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4483 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4484 if (n_selected == 0) {
4485 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4486 } else if (n_selected == 1) {
4487 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4488 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4490 if (repr) {
4491 g_object_set_data( tbl, "repr", repr );
4492 Inkscape::GC::anchor(repr);
4493 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4494 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4495 }
4496 } else {
4497 // FIXME: implement averaging of all parameters for multiple selected
4498 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4499 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4500 sp_arctb_sensitivize( tbl, 1, 0 );
4501 }
4502 }
4505 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4506 {
4507 EgeAdjustmentAction* eact = 0;
4508 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4511 {
4512 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4513 ege_output_action_set_use_markup( act, TRUE );
4514 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4515 g_object_set_data( holder, "mode_action", act );
4516 }
4518 /* Start */
4519 {
4520 eact = create_adjustment_action( "ArcStartAction",
4521 _("Start"), _("Start:"),
4522 _("The angle (in degrees) from the horizontal to the arc's start point"),
4523 "tools.shapes.arc", "start", 0.0,
4524 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4525 -360.0, 360.0, 1.0, 10.0,
4526 0, 0, 0,
4527 sp_arctb_start_value_changed);
4528 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4529 }
4531 /* End */
4532 {
4533 eact = create_adjustment_action( "ArcEndAction",
4534 _("End"), _("End:"),
4535 _("The angle (in degrees) from the horizontal to the arc's end point"),
4536 "tools.shapes.arc", "end", 0.0,
4537 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4538 -360.0, 360.0, 1.0, 10.0,
4539 0, 0, 0,
4540 sp_arctb_end_value_changed);
4541 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4542 }
4544 /* Segments / Pie checkbox */
4545 {
4546 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4548 GtkTreeIter iter;
4549 gtk_list_store_append( model, &iter );
4550 gtk_list_store_set( model, &iter,
4551 0, _("Closed arc"),
4552 1, _("Switch to segment (closed shape with two radii)"),
4553 2, "circle_closed_arc",
4554 -1 );
4556 gtk_list_store_append( model, &iter );
4557 gtk_list_store_set( model, &iter,
4558 0, _("Open Arc"),
4559 1, _("Switch to arc (unclosed shape)"),
4560 2, "circle_open_arc",
4561 -1 );
4563 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4564 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4565 g_object_set_data( holder, "open_action", act );
4567 ege_select_one_action_set_appearance( act, "full" );
4568 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4569 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4570 ege_select_one_action_set_icon_column( act, 2 );
4571 ege_select_one_action_set_icon_size( act, secondarySize );
4572 ege_select_one_action_set_tooltip_column( act, 1 );
4574 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4575 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4576 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4577 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4578 }
4580 /* Make Whole */
4581 {
4582 InkAction* inky = ink_action_new( "ArcResetAction",
4583 _("Make whole"),
4584 _("Make the shape a whole ellipse, not arc or segment"),
4585 "reset_circle",
4586 secondarySize );
4587 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4588 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4589 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4590 g_object_set_data( holder, "make_whole", inky );
4591 }
4593 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4594 // sensitivize make whole and open checkbox
4595 {
4596 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4597 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4598 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4599 }
4602 sigc::connection *connection = new sigc::connection(
4603 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4604 );
4605 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4606 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4607 }
4612 // toggle button callbacks and updaters
4614 //########################
4615 //## Dropper ##
4616 //########################
4618 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4619 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4620 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4621 if ( set_action ) {
4622 if ( gtk_toggle_action_get_active( act ) ) {
4623 gtk_action_set_sensitive( set_action, TRUE );
4624 } else {
4625 gtk_action_set_sensitive( set_action, FALSE );
4626 }
4627 }
4629 spinbutton_defocus(GTK_OBJECT(tbl));
4630 }
4632 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4633 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4634 spinbutton_defocus(GTK_OBJECT(tbl));
4635 }
4638 /**
4639 * Dropper auxiliary toolbar construction and setup.
4640 *
4641 * TODO: Would like to add swatch of current color.
4642 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4643 * can drag and drop places. Will provide a nice mixing palette.
4644 */
4645 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4646 {
4647 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4649 {
4650 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4651 ege_output_action_set_use_markup( act, TRUE );
4652 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4653 }
4655 {
4656 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4657 _("Pick opacity"),
4658 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4659 NULL,
4660 Inkscape::ICON_SIZE_DECORATION );
4661 g_object_set( act, "short_label", _("Pick"), NULL );
4662 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4663 g_object_set_data( holder, "pick_action", act );
4664 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4665 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4666 }
4668 {
4669 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4670 _("Assign opacity"),
4671 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4672 NULL,
4673 Inkscape::ICON_SIZE_DECORATION );
4674 g_object_set( act, "short_label", _("Assign"), NULL );
4675 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4676 g_object_set_data( holder, "set_action", act );
4677 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4678 // make sure it's disabled if we're not picking alpha
4679 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4680 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4681 }
4682 }
4686 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4687 {
4688 {
4689 /* Width */
4690 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4691 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4692 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4693 _("Pen Width"), _("Width:"),
4694 _("The width of the eraser pen (relative to the visible canvas area)"),
4695 "tools.eraser", "width", 15,
4696 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4697 1, 100, 1.0, 0.0,
4698 labels, values, G_N_ELEMENTS(labels),
4699 sp_ddc_width_value_changed, 0.01, 0, 100 );
4700 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4701 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4702 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4703 }
4705 {
4706 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4708 GtkTreeIter iter;
4709 gtk_list_store_append( model, &iter );
4710 gtk_list_store_set( model, &iter,
4711 0, _("Delete"),
4712 1, _("Delete objects touched by the eraser"),
4713 2, "delete_object",
4714 -1 );
4716 gtk_list_store_append( model, &iter );
4717 gtk_list_store_set( model, &iter,
4718 0, _("Cut"),
4719 1, _("Cut out from objects"),
4720 2, "difference",
4721 -1 );
4723 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4724 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4725 g_object_set_data( holder, "eraser_mode_action", act );
4727 ege_select_one_action_set_appearance( act, "full" );
4728 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4729 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4730 ege_select_one_action_set_icon_column( act, 2 );
4731 ege_select_one_action_set_tooltip_column( act, 1 );
4733 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4734 ege_select_one_action_set_active( act, eraserMode );
4735 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4736 }
4738 }
4740 //########################
4741 //## Text Toolbox ##
4742 //########################
4743 /*
4744 static void
4745 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4746 {
4747 //Call back for letter sizing spinbutton
4748 }
4750 static void
4751 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4752 {
4753 //Call back for line height spinbutton
4754 }
4756 static void
4757 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4758 {
4759 //Call back for horizontal kerning spinbutton
4760 }
4762 static void
4763 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4764 {
4765 //Call back for vertical kerning spinbutton
4766 }
4768 static void
4769 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4770 {
4771 //Call back for letter rotation spinbutton
4772 }*/
4774 namespace {
4776 bool popdown_visible = false;
4777 bool popdown_hasfocus = false;
4779 void
4780 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4781 {
4782 SPStyle *query =
4783 sp_style_new (SP_ACTIVE_DOCUMENT);
4785 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4787 int result_family =
4788 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4790 int result_style =
4791 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4793 int result_numbers =
4794 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4796 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4798 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4799 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4800 {
4801 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4803 if (repr)
4804 {
4805 sp_style_read_from_repr (query, repr);
4806 }
4807 else
4808 {
4809 return;
4810 }
4811 }
4813 if (query->text)
4814 {
4815 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4816 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4817 gtk_entry_set_text (GTK_ENTRY (entry), "");
4819 } else if (query->text->font_specification.value || query->text->font_family.value) {
4821 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4823 // Get the font that corresponds
4824 Glib::ustring familyName;
4826 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4827 if (font) {
4828 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4829 font->Unref();
4830 font = NULL;
4831 }
4833 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4835 Gtk::TreePath path;
4836 try {
4837 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4838 } catch (...) {
4839 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4840 return;
4841 }
4843 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4844 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4846 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4848 gtk_tree_selection_select_path (tselection, path.gobj());
4849 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4851 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4852 }
4854 //Size
4855 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4856 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4857 g_object_set_data (tbl, "size-block", gpointer(1));
4858 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4859 g_object_set_data (tbl, "size-block", gpointer(0));
4860 free (str);
4862 //Anchor
4863 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4864 {
4865 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4866 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4867 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4868 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4869 }
4870 else
4871 {
4872 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4873 {
4874 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4875 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4876 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4877 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4878 }
4879 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4880 {
4881 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4882 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4883 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4884 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4885 }
4886 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4887 {
4888 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4889 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4890 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4891 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4892 }
4893 }
4895 //Style
4896 {
4897 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4899 gboolean active = gtk_toggle_button_get_active (button);
4900 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4902 if (active != check)
4903 {
4904 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4905 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4906 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4907 }
4908 }
4910 {
4911 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4913 gboolean active = gtk_toggle_button_get_active (button);
4914 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4916 if (active != check)
4917 {
4918 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4919 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4920 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4921 }
4922 }
4924 //Orientation
4925 //locking both buttons, changing one affect all group (both)
4926 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4927 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4929 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4930 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4932 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4933 {
4934 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4935 }
4936 else
4937 {
4938 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4939 }
4940 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4941 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4942 }
4944 sp_style_unref(query);
4945 }
4947 void
4948 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4949 {
4950 sp_text_toolbox_selection_changed (selection, tbl);
4951 }
4953 void
4954 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4955 {
4956 sp_text_toolbox_selection_changed (NULL, tbl);
4957 }
4959 void
4960 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4961 GObject *tbl)
4962 {
4963 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4964 GtkTreeModel *model = 0;
4965 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4966 GtkTreeIter iter;
4967 char *family = 0;
4969 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4970 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4972 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4973 return;
4974 }
4976 gtk_tree_model_get (model, &iter, 0, &family, -1);
4978 if (g_object_get_data (G_OBJECT (selection), "block"))
4979 {
4980 gtk_entry_set_text (GTK_ENTRY (entry), family);
4981 return;
4982 }
4984 gtk_entry_set_text (GTK_ENTRY (entry), family);
4986 SPStyle *query =
4987 sp_style_new (SP_ACTIVE_DOCUMENT);
4989 int result_fontspec =
4990 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4992 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4994 SPCSSAttr *css = sp_repr_css_attr_new ();
4997 // First try to get the font spec from the stored value
4998 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5000 if (fontSpec.empty()) {
5001 // Construct a new font specification if it does not yet exist
5002 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5003 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5004 fontFromStyle->Unref();
5005 }
5007 if (!fontSpec.empty()) {
5008 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5009 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5010 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5011 if (font) {
5012 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5014 // Set all the these just in case they were altered when finding the best
5015 // match for the new family and old style...
5017 gchar c[256];
5019 font->Family(c, 256);
5020 sp_repr_css_set_property (css, "font-family", c);
5022 font->Attribute( "weight", c, 256);
5023 sp_repr_css_set_property (css, "font-weight", c);
5025 font->Attribute("style", c, 256);
5026 sp_repr_css_set_property (css, "font-style", c);
5028 font->Attribute("stretch", c, 256);
5029 sp_repr_css_set_property (css, "font-stretch", c);
5031 font->Attribute("variant", c, 256);
5032 sp_repr_css_set_property (css, "font-variant", c);
5034 font->Unref();
5035 }
5036 }
5037 }
5039 // If querying returned nothing, set the default style of the tool (for new texts)
5040 if (result_fontspec == QUERY_STYLE_NOTHING)
5041 {
5042 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5043 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5044 }
5045 else
5046 {
5047 sp_desktop_set_style (desktop, css, true, true);
5048 }
5050 sp_style_unref(query);
5052 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5053 _("Text: Change font family"));
5054 sp_repr_css_attr_unref (css);
5055 free (family);
5056 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5058 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5059 }
5061 void
5062 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5063 GObject *tbl)
5064 {
5065 const char *family = gtk_entry_get_text (entry);
5067 try {
5068 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5069 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5070 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5071 gtk_tree_selection_select_path (selection, path.gobj());
5072 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5073 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5074 } catch (...) {
5075 if (family && strlen (family))
5076 {
5077 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5078 }
5079 }
5080 }
5082 void
5083 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5084 gpointer data)
5085 {
5086 if (g_object_get_data (G_OBJECT (button), "block")) return;
5087 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5088 int prop = GPOINTER_TO_INT(data);
5090 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5091 SPCSSAttr *css = sp_repr_css_attr_new ();
5093 switch (prop)
5094 {
5095 case 0:
5096 {
5097 sp_repr_css_set_property (css, "text-anchor", "start");
5098 sp_repr_css_set_property (css, "text-align", "start");
5099 break;
5100 }
5101 case 1:
5102 {
5103 sp_repr_css_set_property (css, "text-anchor", "middle");
5104 sp_repr_css_set_property (css, "text-align", "center");
5105 break;
5106 }
5108 case 2:
5109 {
5110 sp_repr_css_set_property (css, "text-anchor", "end");
5111 sp_repr_css_set_property (css, "text-align", "end");
5112 break;
5113 }
5115 case 3:
5116 {
5117 sp_repr_css_set_property (css, "text-anchor", "start");
5118 sp_repr_css_set_property (css, "text-align", "justify");
5119 break;
5120 }
5121 }
5123 SPStyle *query =
5124 sp_style_new (SP_ACTIVE_DOCUMENT);
5125 int result_numbers =
5126 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5128 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5129 if (result_numbers == QUERY_STYLE_NOTHING)
5130 {
5131 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5132 }
5134 sp_style_unref(query);
5136 sp_desktop_set_style (desktop, css, true, true);
5137 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5138 _("Text: Change alignment"));
5139 sp_repr_css_attr_unref (css);
5141 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5142 }
5144 void
5145 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5146 gpointer data)
5147 {
5148 if (g_object_get_data (G_OBJECT (button), "block")) return;
5150 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5151 SPCSSAttr *css = sp_repr_css_attr_new ();
5152 int prop = GPOINTER_TO_INT(data);
5153 bool active = gtk_toggle_button_get_active (button);
5155 SPStyle *query =
5156 sp_style_new (SP_ACTIVE_DOCUMENT);
5158 int result_fontspec =
5159 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5161 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5162 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5163 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5165 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5166 Glib::ustring newFontSpec = "";
5168 if (fontSpec.empty()) {
5169 // Construct a new font specification if it does not yet exist
5170 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5171 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5172 fontFromStyle->Unref();
5173 }
5175 switch (prop)
5176 {
5177 case 0:
5178 {
5179 if (!fontSpec.empty()) {
5180 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5181 }
5182 if (fontSpec != newFontSpec) {
5183 // Don't even set the bold if the font didn't exist on the system
5184 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5185 }
5186 break;
5187 }
5189 case 1:
5190 {
5191 if (!fontSpec.empty()) {
5192 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5193 }
5194 if (fontSpec != newFontSpec) {
5195 // Don't even set the italic if the font didn't exist on the system
5196 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5197 }
5198 break;
5199 }
5200 }
5202 if (!newFontSpec.empty()) {
5203 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5204 }
5206 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5207 if (result_fontspec == QUERY_STYLE_NOTHING)
5208 {
5209 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5210 }
5212 sp_style_unref(query);
5214 sp_desktop_set_style (desktop, css, true, true);
5215 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5216 _("Text: Change font style"));
5217 sp_repr_css_attr_unref (css);
5219 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5220 }
5222 void
5223 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5224 gpointer data)
5225 {
5226 if (g_object_get_data (G_OBJECT (button), "block")) {
5227 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5228 return;
5229 }
5231 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5232 SPCSSAttr *css = sp_repr_css_attr_new ();
5233 int prop = GPOINTER_TO_INT(data);
5235 switch (prop)
5236 {
5237 case 0:
5238 {
5239 sp_repr_css_set_property (css, "writing-mode", "lr");
5240 break;
5241 }
5243 case 1:
5244 {
5245 sp_repr_css_set_property (css, "writing-mode", "tb");
5246 break;
5247 }
5248 }
5250 SPStyle *query =
5251 sp_style_new (SP_ACTIVE_DOCUMENT);
5252 int result_numbers =
5253 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5255 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5256 if (result_numbers == QUERY_STYLE_NOTHING)
5257 {
5258 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5259 }
5261 sp_desktop_set_style (desktop, css, true, true);
5262 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5263 _("Text: Change orientation"));
5264 sp_repr_css_attr_unref (css);
5266 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5267 }
5269 gboolean
5270 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5271 {
5272 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5273 if (!desktop) return FALSE;
5275 switch (get_group0_keyval (event)) {
5276 case GDK_Escape: // defocus
5277 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5278 sp_text_toolbox_selection_changed (NULL, tbl); // update
5279 return TRUE; // I consumed the event
5280 break;
5281 }
5282 return FALSE;
5283 }
5285 gboolean
5286 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5287 {
5288 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5289 if (!desktop) return FALSE;
5291 switch (get_group0_keyval (event)) {
5292 case GDK_KP_Enter:
5293 case GDK_Return:
5294 case GDK_Escape: // defocus
5295 gtk_widget_hide (w);
5296 popdown_visible = false;
5297 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5298 return TRUE; // I consumed the event
5299 break;
5300 case GDK_w:
5301 case GDK_W:
5302 if (event->state & GDK_CONTROL_MASK) {
5303 gtk_widget_hide (w);
5304 popdown_visible = false;
5305 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5306 return TRUE; // I consumed the event
5307 }
5308 break;
5309 }
5310 return FALSE;
5311 }
5314 void
5315 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5316 GObject *tbl)
5317 {
5318 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5320 if (g_object_get_data (tbl, "size-block")) return;
5322 // If this is not from selecting a size in the list (in which case get_active will give the
5323 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5324 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5325 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5326 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5327 return;
5329 gchar *endptr;
5330 gdouble value = -1;
5331 char *text = gtk_combo_box_get_active_text (cbox);
5332 if (text) {
5333 value = g_strtod (text, &endptr);
5334 if (endptr == text) // conversion failed, non-numeric input
5335 value = -1;
5336 free (text);
5337 }
5338 if (value <= 0) {
5339 return; // could not parse value
5340 }
5342 SPCSSAttr *css = sp_repr_css_attr_new ();
5343 Inkscape::CSSOStringStream osfs;
5344 osfs << value;
5345 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5347 SPStyle *query =
5348 sp_style_new (SP_ACTIVE_DOCUMENT);
5349 int result_numbers =
5350 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5352 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5353 if (result_numbers == QUERY_STYLE_NOTHING)
5354 {
5355 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5356 }
5358 sp_style_unref(query);
5360 sp_desktop_set_style (desktop, css, true, true);
5361 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5362 _("Text: Change font size"));
5363 sp_repr_css_attr_unref (css);
5365 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5366 }
5368 gboolean
5369 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5370 {
5371 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5372 if (!desktop) return FALSE;
5374 if (!g_object_get_data (tbl, "esc-pressed")) {
5375 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5376 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5377 sp_text_toolbox_size_changed (cbox, tbl);
5378 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5379 }
5380 return FALSE; // I consumed the event
5381 }
5384 gboolean
5385 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5386 {
5387 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5388 if (!desktop) return FALSE;
5390 switch (get_group0_keyval (event)) {
5391 case GDK_Escape: // defocus
5392 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5393 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5394 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5395 return TRUE; // I consumed the event
5396 break;
5397 case GDK_Return: // defocus
5398 case GDK_KP_Enter:
5399 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5400 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5401 sp_text_toolbox_size_changed (cbox, tbl);
5402 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5403 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5404 return TRUE; // I consumed the event
5405 break;
5406 }
5407 return FALSE;
5408 }
5410 void
5411 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5412 GObject *tbl)
5413 {
5414 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5415 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5416 int x, y;
5418 if (!popdown_visible)
5419 {
5420 gdk_window_get_origin (widget->window, &x, &y);
5421 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5422 gtk_widget_show_all (popdown);
5423 //sp_transientize (popdown);
5425 gdk_pointer_grab (widget->window, TRUE,
5426 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5427 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5428 GDK_POINTER_MOTION_MASK),
5429 NULL, NULL, GDK_CURRENT_TIME);
5431 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5433 popdown_visible = true;
5434 }
5435 else
5436 {
5437 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5438 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5439 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5440 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5441 gtk_widget_hide (popdown);
5442 popdown_visible = false;
5443 }
5444 }
5446 gboolean
5447 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5448 GdkEventFocus */*event*/,
5449 GObject */*tbl*/)
5450 {
5451 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5452 return FALSE;
5453 }
5455 gboolean
5456 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5457 GdkEventFocus */*event*/,
5458 GObject */*tbl*/)
5459 {
5460 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5462 if (popdown_hasfocus) {
5463 gtk_widget_hide (popdown);
5464 popdown_hasfocus = false;
5465 popdown_visible = false;
5466 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5467 return TRUE;
5468 }
5469 return FALSE;
5470 }
5472 gboolean
5473 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5474 GdkEventFocus */*event*/,
5475 GObject */*tbl*/)
5476 {
5477 popdown_hasfocus = true;
5478 return TRUE;
5479 }
5482 void
5483 cell_data_func (GtkTreeViewColumn */*column*/,
5484 GtkCellRenderer *cell,
5485 GtkTreeModel *tree_model,
5486 GtkTreeIter *iter,
5487 gpointer /*data*/)
5488 {
5489 char *family,
5490 *family_escaped,
5491 *sample_escaped;
5493 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5495 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5497 family_escaped = g_markup_escape_text (family, -1);
5498 sample_escaped = g_markup_escape_text (sample, -1);
5500 std::stringstream markup;
5501 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5502 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5504 free (family);
5505 free (family_escaped);
5506 free (sample_escaped);
5507 }
5509 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5510 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5511 if (completion) {
5512 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5513 g_object_unref (completion);
5514 }
5515 }
5517 GtkWidget*
5518 sp_text_toolbox_new (SPDesktop *desktop)
5519 {
5520 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5521 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5523 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5524 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5526 GtkTooltips *tt = gtk_tooltips_new();
5527 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5529 ////////////Family
5530 //Window
5531 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5532 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5534 //Entry
5535 GtkWidget *entry = gtk_entry_new ();
5536 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5537 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5538 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5539 gtk_entry_completion_set_text_column (completion, 0);
5540 gtk_entry_completion_set_minimum_key_length (completion, 1);
5541 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5542 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5543 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5544 gtk_toolbar_append_widget( tbl, entry, "", "" );
5545 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5547 //Button
5548 GtkWidget *button = gtk_button_new ();
5549 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5550 gtk_toolbar_append_widget( tbl, button, "", "");
5552 //Popdown
5553 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5554 GtkWidget *treeview = gtk_tree_view_new ();
5556 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5557 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5558 gtk_tree_view_column_pack_start (column, cell, FALSE);
5559 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5560 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5561 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5563 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5564 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5565 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5567 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5569 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5570 gtk_container_add (GTK_CONTAINER (sw), treeview);
5572 gtk_container_add (GTK_CONTAINER (window), sw);
5573 gtk_widget_set_size_request (window, 300, 450);
5575 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5576 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5577 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5579 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5581 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5582 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5583 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5585 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5586 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5588 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5589 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5590 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5591 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5592 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5594 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5595 GtkWidget *box = gtk_event_box_new ();
5596 gtk_container_add (GTK_CONTAINER (box), image);
5597 gtk_toolbar_append_widget( tbl, box, "", "");
5598 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5599 GtkTooltips *tooltips = gtk_tooltips_new ();
5600 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5601 gtk_widget_hide (GTK_WIDGET (box));
5602 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5604 ////////////Size
5605 const char *sizes[] = {
5606 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5607 "16", "18", "20", "22", "24", "28",
5608 "32", "36", "40", "48", "56", "64", "72", "144"
5609 };
5611 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5612 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5613 gtk_widget_set_size_request (cbox, 80, -1);
5614 gtk_toolbar_append_widget( tbl, cbox, "", "");
5615 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5616 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5617 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5618 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5620 ////////////Text anchor
5621 GtkWidget *group = gtk_radio_button_new (NULL);
5622 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5623 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5625 // left
5626 GtkWidget *rbutton = group;
5627 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5628 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5629 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5631 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5632 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5633 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5634 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5636 // center
5637 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5638 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5639 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5640 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5642 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5643 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5644 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5645 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5647 // right
5648 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5649 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5650 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5651 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5653 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5654 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5655 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5656 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5658 // fill
5659 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5660 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5661 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5662 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5664 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5665 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5666 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5667 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5669 gtk_toolbar_append_widget( tbl, row, "", "");
5671 //spacer
5672 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5674 ////////////Text style
5675 row = gtk_hbox_new (FALSE, 4);
5677 // bold
5678 rbutton = gtk_toggle_button_new ();
5679 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5680 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5681 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5682 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5684 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5685 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5686 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5688 // italic
5689 rbutton = gtk_toggle_button_new ();
5690 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5691 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5692 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5693 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5695 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5696 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5697 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5699 gtk_toolbar_append_widget( tbl, row, "", "");
5701 //spacer
5702 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5704 ////////////Text orientation
5705 group = gtk_radio_button_new (NULL);
5706 row = gtk_hbox_new (FALSE, 4);
5707 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5709 // horizontal
5710 rbutton = group;
5711 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5712 gtk_container_add (GTK_CONTAINER (rbutton),
5713 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5714 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5715 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5717 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5718 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5719 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5721 // vertical
5722 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5723 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5724 gtk_container_add (GTK_CONTAINER (rbutton),
5725 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5726 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5727 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5729 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5730 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5731 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5732 gtk_toolbar_append_widget( tbl, row, "", "" );
5735 //watch selection
5736 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5738 sigc::connection *c_selection_changed =
5739 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5740 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5741 pool->add_connection ("selection-changed", c_selection_changed);
5743 sigc::connection *c_selection_modified =
5744 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5745 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5746 pool->add_connection ("selection-modified", c_selection_modified);
5748 sigc::connection *c_subselection_changed =
5749 new sigc::connection (desktop->connectToolSubselectionChanged
5750 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5751 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5753 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5756 gtk_widget_show_all( GTK_WIDGET(tbl) );
5758 return GTK_WIDGET(tbl);
5759 } // end of sp_text_toolbox_new()
5761 }//<unnamed> namespace
5764 //#########################
5765 //## Connector ##
5766 //#########################
5768 static void sp_connector_path_set_avoid(void)
5769 {
5770 cc_selection_set_avoid(true);
5771 }
5774 static void sp_connector_path_set_ignore(void)
5775 {
5776 cc_selection_set_avoid(false);
5777 }
5781 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5782 {
5783 // quit if run by the _changed callbacks
5784 if (g_object_get_data( tbl, "freeze" )) {
5785 return;
5786 }
5788 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5789 SPDocument *doc = sp_desktop_document(desktop);
5791 if (!sp_document_get_undo_sensitive(doc))
5792 {
5793 return;
5794 }
5796 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5798 if ( repr->attribute("inkscape:connector-spacing") ) {
5799 gdouble priorValue = gtk_adjustment_get_value(adj);
5800 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5801 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5802 return;
5803 }
5804 } else if ( adj->value == defaultConnSpacing ) {
5805 return;
5806 }
5808 // in turn, prevent callbacks from responding
5809 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5811 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5812 SP_OBJECT(desktop->namedview)->updateRepr();
5814 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5815 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5816 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5817 NR::Matrix m = NR::identity();
5818 avoid_item_move(&m, item);
5819 }
5821 if (items) {
5822 g_slist_free(items);
5823 }
5825 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5826 _("Change connector spacing"));
5828 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5830 spinbutton_defocus(GTK_OBJECT(tbl));
5831 }
5833 static void sp_connector_graph_layout(void)
5834 {
5835 if (!SP_ACTIVE_DESKTOP) return;
5837 // hack for clones, see comment in align-and-distribute.cpp
5838 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5839 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5841 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5843 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5845 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5846 }
5848 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5849 {
5850 if ( gtk_toggle_action_get_active( act ) ) {
5851 prefs_set_string_attribute("tools.connector", "directedlayout",
5852 "true");
5853 } else {
5854 prefs_set_string_attribute("tools.connector", "directedlayout",
5855 "false");
5856 }
5857 }
5859 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5860 {
5861 if ( gtk_toggle_action_get_active( act ) ) {
5862 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5863 "true");
5864 } else {
5865 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5866 "false");
5867 }
5868 }
5871 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5872 {
5873 prefs_set_double_attribute("tools.connector", "length", adj->value);
5874 spinbutton_defocus(GTK_OBJECT(tbl));
5875 }
5877 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5878 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5879 bool /*is_interactive*/, gpointer data)
5880 {
5881 GtkWidget *tbl = GTK_WIDGET(data);
5883 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5884 return;
5885 }
5886 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5887 return;
5888 }
5890 GtkAdjustment *adj = (GtkAdjustment*)
5891 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5892 gdouble spacing = defaultConnSpacing;
5893 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5895 gtk_adjustment_set_value(adj, spacing);
5896 }
5899 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5900 NULL, /* child_added */
5901 NULL, /* child_removed */
5902 connector_tb_event_attr_changed,
5903 NULL, /* content_changed */
5904 NULL /* order_changed */
5905 };
5908 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5909 {
5910 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5912 {
5913 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5914 _("Avoid"),
5915 _("Make connectors avoid selected objects"),
5916 "connector_avoid",
5917 secondarySize );
5918 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5919 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5920 }
5922 {
5923 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5924 _("Ignore"),
5925 _("Make connectors ignore selected objects"),
5926 "connector_ignore",
5927 secondarySize );
5928 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5929 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5930 }
5932 EgeAdjustmentAction* eact = 0;
5934 // Spacing spinbox
5935 eact = create_adjustment_action( "ConnectorSpacingAction",
5936 _("Connector Spacing"), _("Spacing:"),
5937 _("The amount of space left around objects by auto-routing connectors"),
5938 "tools.connector", "spacing", defaultConnSpacing,
5939 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5940 0, 100, 1.0, 10.0,
5941 0, 0, 0,
5942 connector_spacing_changed, 1, 0 );
5943 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5945 // Graph (connector network) layout
5946 {
5947 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5948 _("Graph"),
5949 _("Nicely arrange selected connector network"),
5950 "graph_layout",
5951 secondarySize );
5952 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5953 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5954 }
5956 // Default connector length spinbox
5957 eact = create_adjustment_action( "ConnectorLengthAction",
5958 _("Connector Length"), _("Length:"),
5959 _("Ideal length for connectors when layout is applied"),
5960 "tools.connector", "length", 100,
5961 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5962 10, 1000, 10.0, 100.0,
5963 0, 0, 0,
5964 connector_length_changed, 1, 0 );
5965 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5968 // Directed edges toggle button
5969 {
5970 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5971 _("Downwards"),
5972 _("Make connectors with end-markers (arrows) point downwards"),
5973 "directed_graph",
5974 Inkscape::ICON_SIZE_DECORATION );
5975 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5977 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5978 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5979 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5981 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5982 }
5984 // Avoid overlaps toggle button
5985 {
5986 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5987 _("Remove overlaps"),
5988 _("Do not allow overlapping shapes"),
5989 "remove_overlaps",
5990 Inkscape::ICON_SIZE_DECORATION );
5991 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5993 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5994 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5995 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5997 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5998 }
6000 // Code to watch for changes to the connector-spacing attribute in
6001 // the XML.
6002 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6003 g_assert(repr != NULL);
6005 purge_repr_listener( holder, holder );
6007 if (repr) {
6008 g_object_set_data( holder, "repr", repr );
6009 Inkscape::GC::anchor(repr);
6010 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6011 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6012 }
6013 } // end of sp_connector_toolbox_prep()
6016 //#########################
6017 //## Paintbucket ##
6018 //#########################
6020 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6021 {
6022 gint channels = ege_select_one_action_get_active( act );
6023 flood_channels_set_channels( channels );
6024 }
6026 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6027 {
6028 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6029 }
6031 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6032 {
6033 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6034 }
6036 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6037 {
6038 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6039 SPUnit const *unit = tracker->getActiveUnit();
6041 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6043 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6044 }
6046 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
6047 {
6048 // FIXME: make defaults settable via Inkscape Options
6049 struct KeyValue {
6050 char const *key;
6051 double value;
6052 } const key_values[] = {
6053 {"threshold", 15},
6054 {"offset", 0.0}
6055 };
6057 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6058 KeyValue const &kv = key_values[i];
6059 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
6060 if ( adj ) {
6061 gtk_adjustment_set_value(adj, kv.value);
6062 }
6063 }
6065 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
6066 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6067 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
6068 ege_select_one_action_set_active( autogap_action, 0 );
6069 }
6071 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6072 {
6073 EgeAdjustmentAction* eact = 0;
6075 {
6076 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6078 GList* items = 0;
6079 gint count = 0;
6080 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6081 {
6082 GtkTreeIter iter;
6083 gtk_list_store_append( model, &iter );
6084 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6085 count++;
6086 }
6087 g_list_free( items );
6088 items = 0;
6089 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6090 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6091 ege_select_one_action_set_appearance( act1, "compact" );
6092 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6093 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6094 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6095 g_object_set_data( holder, "channels_action", act1 );
6096 }
6098 // Spacing spinbox
6099 {
6100 eact = create_adjustment_action(
6101 "ThresholdAction",
6102 _("Fill Threshold"), _("Threshold:"),
6103 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6104 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6105 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6106 0, 0, 0,
6107 paintbucket_threshold_changed, 1, 0 );
6109 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6110 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6111 }
6113 // Create the units menu.
6114 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6115 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6116 if (stored_unit)
6117 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6118 g_object_set_data( holder, "tracker", tracker );
6119 {
6120 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6121 gtk_action_group_add_action( mainActions, act );
6122 }
6124 // Offset spinbox
6125 {
6126 eact = create_adjustment_action(
6127 "OffsetAction",
6128 _("Grow/shrink by"), _("Grow/shrink by:"),
6129 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6130 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6131 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6132 0, 0, 0,
6133 paintbucket_offset_changed, 1, 2);
6134 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6136 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6137 }
6139 /* Auto Gap */
6140 {
6141 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6143 GList* items = 0;
6144 gint count = 0;
6145 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6146 {
6147 GtkTreeIter iter;
6148 gtk_list_store_append( model, &iter );
6149 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6150 count++;
6151 }
6152 g_list_free( items );
6153 items = 0;
6154 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6155 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6156 ege_select_one_action_set_appearance( act2, "compact" );
6157 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6158 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6159 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6160 g_object_set_data( holder, "autogap_action", act2 );
6161 }
6163 /* Reset */
6164 {
6165 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6166 _("Defaults"),
6167 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6168 GTK_STOCK_CLEAR );
6169 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6170 gtk_action_group_add_action( mainActions, act );
6171 gtk_action_set_sensitive( act, TRUE );
6172 }
6174 }
6176 /*
6177 Local Variables:
6178 mode:c++
6179 c-file-style:"stroustrup"
6180 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6181 indent-tabs-mode:nil
6182 fill-column:99
6183 End:
6184 */
6185 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :