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 "tools-switch.h"
84 #include "selection.h"
85 #include "selection-chemistry.h"
86 #include "document-private.h"
87 #include "desktop-style.h"
88 #include "../libnrtype/font-lister.h"
89 #include "../libnrtype/font-instance.h"
90 #include "../connection-pool.h"
91 #include "../prefs-utils.h"
92 #include "../inkscape-stock.h"
93 #include "icon.h"
94 #include "graphlayout/graphlayout.h"
95 #include "interface.h"
96 #include "shortcuts.h"
98 #include "mod360.h"
100 #include "toolbox.h"
102 #include "flood-context.h"
104 #include "ink-action.h"
105 #include "ege-adjustment-action.h"
106 #include "ege-output-action.h"
107 #include "ege-select-one-action.h"
108 #include "helper/unit-tracker.h"
110 #include "svg/css-ostringstream.h"
112 #include "widgets/calligraphic-profile-rename.h"
114 using Inkscape::UnitTracker;
116 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
117 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
119 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
132 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
139 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
140 static Inkscape::IconSize sizeChoices[] = {
141 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
142 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
143 Inkscape::ICON_SIZE_MENU
144 };
145 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
146 return sizeChoices[index];
147 }
149 static struct {
150 gchar const *type_name;
151 gchar const *data_name;
152 sp_verb_t verb;
153 sp_verb_t doubleclick_verb;
154 } const tools[] = {
155 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
156 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
157 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
158 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
159 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
160 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
161 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
162 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
163 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
164 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
165 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
166 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
167 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
168 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
169 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
170 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
171 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
172 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
173 { NULL, NULL, 0, 0 }
174 };
176 static struct {
177 gchar const *type_name;
178 gchar const *data_name;
179 GtkWidget *(*create_func)(SPDesktop *desktop);
180 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
181 gchar const *ui_name;
182 gint swatch_verb_id;
183 gchar const *swatch_tool;
184 gchar const *swatch_tip;
185 } const aux_toolboxes[] = {
186 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
187 SP_VERB_INVALID, 0, 0},
188 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
189 SP_VERB_INVALID, 0, 0},
190 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
191 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
192 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
193 SP_VERB_INVALID, 0, 0},
194 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
195 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
196 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
197 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
198 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
199 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
200 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
201 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
202 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
203 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
204 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
205 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
206 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
207 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
208 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
209 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
210 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
211 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
212 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
213 SP_VERB_INVALID, 0, 0},
214 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
215 SP_VERB_INVALID, 0, 0},
216 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
217 SP_VERB_INVALID, 0, 0},
218 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
219 SP_VERB_INVALID, 0, 0},
220 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
221 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
222 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
223 };
225 #define TOOLBAR_SLIDER_HINT "full"
227 static gchar const * ui_descr =
228 "<ui>"
229 " <toolbar name='SelectToolbar'>"
230 " <toolitem action='EditSelectAll' />"
231 " <toolitem action='EditSelectAllInAllLayers' />"
232 " <toolitem action='EditDeselect' />"
233 " <separator />"
234 " <toolitem action='ObjectRotate90CCW' />"
235 " <toolitem action='ObjectRotate90' />"
236 " <toolitem action='ObjectFlipHorizontally' />"
237 " <toolitem action='ObjectFlipVertically' />"
238 " <separator />"
239 " <toolitem action='SelectionToBack' />"
240 " <toolitem action='SelectionLower' />"
241 " <toolitem action='SelectionRaise' />"
242 " <toolitem action='SelectionToFront' />"
243 " <separator />"
244 " <toolitem action='XAction' />"
245 " <toolitem action='YAction' />"
246 " <toolitem action='WidthAction' />"
247 " <toolitem action='LockAction' />"
248 " <toolitem action='HeightAction' />"
249 " <toolitem action='UnitsAction' />"
250 " <separator />"
251 " <toolitem action='transform_affect_label' />"
252 " <toolitem action='transform_stroke' />"
253 " <toolitem action='transform_corners' />"
254 " <toolitem action='transform_gradient' />"
255 " <toolitem action='transform_pattern' />"
256 " </toolbar>"
258 " <toolbar name='NodeToolbar'>"
259 " <toolitem action='NodeInsertAction' />"
260 " <toolitem action='NodeDeleteAction' />"
261 " <separator />"
262 " <toolitem action='NodeJoinAction' />"
263 " <toolitem action='NodeBreakAction' />"
264 " <separator />"
265 " <toolitem action='NodeJoinSegmentAction' />"
266 " <toolitem action='NodeDeleteSegmentAction' />"
267 " <separator />"
268 " <toolitem action='NodeCuspAction' />"
269 " <toolitem action='NodeSmoothAction' />"
270 " <toolitem action='NodeSymmetricAction' />"
271 " <separator />"
272 " <toolitem action='NodeLineAction' />"
273 " <toolitem action='NodeCurveAction' />"
274 " <separator />"
275 " <toolitem action='ObjectToPath' />"
276 " <toolitem action='StrokeToPath' />"
277 " <separator />"
278 " <toolitem action='NodeXAction' />"
279 " <toolitem action='NodeYAction' />"
280 " <toolitem action='NodeUnitsAction' />"
281 " <separator />"
282 " <toolitem action='ObjectEditClipPathAction' />"
283 " <toolitem action='ObjectEditMaskPathAction' />"
284 " <toolitem action='EditNextLPEParameterAction' />"
285 " <separator />"
286 " <toolitem action='NodesShowHandlesAction' />"
287 " <toolitem action='NodesShowHelperpath' />"
288 " </toolbar>"
290 " <toolbar name='TweakToolbar'>"
291 " <toolitem action='TweakWidthAction' />"
292 " <separator />"
293 " <toolitem action='TweakForceAction' />"
294 " <toolitem action='TweakPressureAction' />"
295 " <separator />"
296 " <toolitem action='TweakModeAction' />"
297 " <separator />"
298 " <toolitem action='TweakFidelityAction' />"
299 " <separator />"
300 " <toolitem action='TweakChannelsLabel' />"
301 " <toolitem action='TweakDoH' />"
302 " <toolitem action='TweakDoS' />"
303 " <toolitem action='TweakDoL' />"
304 " <toolitem action='TweakDoO' />"
305 " </toolbar>"
307 " <toolbar name='ZoomToolbar'>"
308 " <toolitem action='ZoomIn' />"
309 " <toolitem action='ZoomOut' />"
310 " <separator />"
311 " <toolitem action='Zoom1:0' />"
312 " <toolitem action='Zoom1:2' />"
313 " <toolitem action='Zoom2:1' />"
314 " <separator />"
315 " <toolitem action='ZoomSelection' />"
316 " <toolitem action='ZoomDrawing' />"
317 " <toolitem action='ZoomPage' />"
318 " <toolitem action='ZoomPageWidth' />"
319 " <separator />"
320 " <toolitem action='ZoomPrev' />"
321 " <toolitem action='ZoomNext' />"
322 " </toolbar>"
324 " <toolbar name='StarToolbar'>"
325 " <separator />"
326 " <toolitem action='StarStateAction' />"
327 " <separator />"
328 " <toolitem action='FlatAction' />"
329 " <separator />"
330 " <toolitem action='MagnitudeAction' />"
331 " <toolitem action='SpokeAction' />"
332 " <toolitem action='RoundednessAction' />"
333 " <toolitem action='RandomizationAction' />"
334 " <separator />"
335 " <toolitem action='StarResetAction' />"
336 " </toolbar>"
338 " <toolbar name='RectToolbar'>"
339 " <toolitem action='RectStateAction' />"
340 " <toolitem action='RectWidthAction' />"
341 " <toolitem action='RectHeightAction' />"
342 " <toolitem action='RadiusXAction' />"
343 " <toolitem action='RadiusYAction' />"
344 " <toolitem action='RectUnitsAction' />"
345 " <separator />"
346 " <toolitem action='RectResetAction' />"
347 " </toolbar>"
349 " <toolbar name='3DBoxToolbar'>"
350 " <toolitem action='3DBoxAngleXAction' />"
351 " <toolitem action='3DBoxVPXStateAction' />"
352 " <separator />"
353 " <toolitem action='3DBoxAngleYAction' />"
354 " <toolitem action='3DBoxVPYStateAction' />"
355 " <separator />"
356 " <toolitem action='3DBoxAngleZAction' />"
357 " <toolitem action='3DBoxVPZStateAction' />"
358 " </toolbar>"
360 " <toolbar name='SpiralToolbar'>"
361 " <toolitem action='SpiralStateAction' />"
362 " <toolitem action='SpiralRevolutionAction' />"
363 " <toolitem action='SpiralExpansionAction' />"
364 " <toolitem action='SpiralT0Action' />"
365 " <separator />"
366 " <toolitem action='SpiralResetAction' />"
367 " </toolbar>"
369 " <toolbar name='PenToolbar'>"
370 " <toolitem action='FreehandModeActionPen' />"
371 " <separator />"
372 " <toolitem action='SetPenShapeAction'/>"
373 " </toolbar>"
375 " <toolbar name='PencilToolbar'>"
376 " <toolitem action='FreehandModeActionPencil' />"
377 " <separator />"
378 " <toolitem action='PencilToleranceAction' />"
379 " <separator />"
380 " <toolitem action='PencilResetAction' />"
381 " <separator />"
382 " <toolitem action='SetPencilShapeAction'/>"
383 " </toolbar>"
385 " <toolbar name='CalligraphyToolbar'>"
386 " <separator />"
387 " <toolitem action='SetProfileAction'/>"
388 " <separator />"
389 " <toolitem action='CalligraphyWidthAction' />"
390 " <toolitem action='PressureAction' />"
391 " <toolitem action='TraceAction' />"
392 " <toolitem action='ThinningAction' />"
393 " <separator />"
394 " <toolitem action='AngleAction' />"
395 " <toolitem action='TiltAction' />"
396 " <toolitem action='FixationAction' />"
397 " <separator />"
398 " <toolitem action='CapRoundingAction' />"
399 " <separator />"
400 " <toolitem action='TremorAction' />"
401 " <toolitem action='WiggleAction' />"
402 " <toolitem action='MassAction' />"
403 " <separator />"
404 " </toolbar>"
406 " <toolbar name='ArcToolbar'>"
407 " <toolitem action='ArcStateAction' />"
408 " <separator />"
409 " <toolitem action='ArcStartAction' />"
410 " <toolitem action='ArcEndAction' />"
411 " <separator />"
412 " <toolitem action='ArcOpenAction' />"
413 " <separator />"
414 " <toolitem action='ArcResetAction' />"
415 " <separator />"
416 " </toolbar>"
418 " <toolbar name='PaintbucketToolbar'>"
419 " <toolitem action='ChannelsAction' />"
420 " <separator />"
421 " <toolitem action='ThresholdAction' />"
422 " <separator />"
423 " <toolitem action='OffsetAction' />"
424 " <toolitem action='PaintbucketUnitsAction' />"
425 " <separator />"
426 " <toolitem action='AutoGapAction' />"
427 " <separator />"
428 " <toolitem action='PaintbucketResetAction' />"
429 " </toolbar>"
431 " <toolbar name='EraserToolbar'>"
432 " <toolitem action='EraserWidthAction' />"
433 " <separator />"
434 " <toolitem action='EraserModeAction' />"
435 " </toolbar>"
437 " <toolbar name='DropperToolbar'>"
438 " <toolitem action='DropperOpacityAction' />"
439 " <toolitem action='DropperPickAlphaAction' />"
440 " <toolitem action='DropperSetAlphaAction' />"
441 " </toolbar>"
443 " <toolbar name='ConnectorToolbar'>"
444 " <toolitem action='ConnectorAvoidAction' />"
445 " <toolitem action='ConnectorIgnoreAction' />"
446 " <toolitem action='ConnectorSpacingAction' />"
447 " <toolitem action='ConnectorGraphAction' />"
448 " <toolitem action='ConnectorLengthAction' />"
449 " <toolitem action='ConnectorDirectedAction' />"
450 " <toolitem action='ConnectorOverlapAction' />"
451 " </toolbar>"
453 "</ui>"
454 ;
456 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
458 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
460 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
461 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
463 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
464 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
466 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
467 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
470 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
471 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
472 Inkscape::UI::View::View *view, GtkTooltips *tt);
474 class VerbAction : public Gtk::Action {
475 public:
476 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
478 virtual ~VerbAction();
479 virtual void set_active(bool active = true);
481 protected:
482 virtual Gtk::Widget* create_menu_item_vfunc();
483 virtual Gtk::Widget* create_tool_item_vfunc();
485 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
486 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
488 virtual void on_activate();
490 private:
491 Inkscape::Verb* verb;
492 Inkscape::Verb* verb2;
493 Inkscape::UI::View::View *view;
494 GtkTooltips *tooltips;
495 bool active;
497 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
498 };
501 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
502 {
503 Glib::RefPtr<VerbAction> result;
504 SPAction *action = verb->get_action(view);
505 if ( action ) {
506 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
507 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
508 }
510 return result;
511 }
513 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
514 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
515 verb(verb),
516 verb2(verb2),
517 view(view),
518 tooltips(tooltips),
519 active(false)
520 {
521 }
523 VerbAction::~VerbAction()
524 {
525 }
527 Gtk::Widget* VerbAction::create_menu_item_vfunc()
528 {
529 // First call in to get the icon rendered if present in SVG
530 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
531 delete widget;
532 widget = 0;
534 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
535 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
536 return widg;
537 }
539 Gtk::Widget* VerbAction::create_tool_item_vfunc()
540 {
541 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
542 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
543 GtkWidget* toolbox = 0;
544 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
545 SP_BUTTON_TYPE_TOGGLE,
546 verb,
547 verb2,
548 view,
549 tooltips );
550 if ( active ) {
551 sp_button_toggle_set_down( SP_BUTTON(button), active);
552 }
553 gtk_widget_show_all( button );
554 Gtk::Widget* wrapped = Glib::wrap(button);
555 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
556 holder->add(*wrapped);
558 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
559 return holder;
560 }
562 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
563 {
564 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
565 Gtk::Action::connect_proxy_vfunc(proxy);
566 }
568 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
569 {
570 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
571 Gtk::Action::disconnect_proxy_vfunc(proxy);
572 }
574 void VerbAction::set_active(bool active)
575 {
576 this->active = active;
577 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
578 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
579 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
580 if (ti) {
581 // *should* have one child that is the SPButton
582 Gtk::Widget* child = ti->get_child();
583 if ( child && SP_IS_BUTTON(child->gobj()) ) {
584 SPButton* button = SP_BUTTON(child->gobj());
585 sp_button_toggle_set_down( button, active );
586 }
587 }
588 }
589 }
591 void VerbAction::on_activate()
592 {
593 if ( verb ) {
594 SPAction *action = verb->get_action(view);
595 if ( action ) {
596 sp_action_perform(action, 0);
597 }
598 }
599 }
601 /* Global text entry widgets necessary for update */
602 /* GtkWidget *dropper_rgb_entry,
603 *dropper_opacity_entry ; */
604 // should be made a private member once this is converted to class
606 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
607 connection->disconnect();
608 delete connection;
609 }
611 static void purge_repr_listener( GObject* obj, GObject* tbl )
612 {
613 (void)obj;
614 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
615 if (oldrepr) { // remove old listener
616 sp_repr_remove_listener_by_data(oldrepr, tbl);
617 Inkscape::GC::release(oldrepr);
618 oldrepr = 0;
619 g_object_set_data( tbl, "repr", NULL );
620 }
621 }
623 GtkWidget *
624 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
625 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
626 Inkscape::UI::View::View *view, GtkTooltips *tt)
627 {
628 SPAction *action = verb->get_action(view);
629 if (!action) return NULL;
631 SPAction *doubleclick_action;
632 if (doubleclick_verb)
633 doubleclick_action = doubleclick_verb->get_action(view);
634 else
635 doubleclick_action = NULL;
637 /* fixme: Handle sensitive/unsensitive */
638 /* fixme: Implement sp_button_new_from_action */
639 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
640 gtk_widget_show(b);
643 unsigned int shortcut = sp_shortcut_get_primary(verb);
644 if (shortcut) {
645 gchar key[256];
646 sp_ui_shortcut_string(shortcut, key);
647 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
648 if ( t ) {
649 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
650 }
651 g_free(tip);
652 } else {
653 if ( t ) {
654 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
655 }
656 }
658 return b;
659 }
662 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
663 {
664 SPAction* targetAction = SP_ACTION(user_data);
665 if ( targetAction ) {
666 sp_action_perform( targetAction, NULL );
667 }
668 }
670 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
671 {
672 if ( data ) {
673 GtkAction* act = GTK_ACTION(data);
674 gtk_action_set_sensitive( act, sensitive );
675 }
676 }
678 static SPActionEventVector action_event_vector = {
679 {NULL},
680 NULL,
681 NULL,
682 sp_action_action_set_sensitive,
683 NULL,
684 NULL
685 };
687 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
688 {
689 GtkAction* act = 0;
691 SPAction* targetAction = verb->get_action(view);
692 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
693 act = GTK_ACTION(inky);
694 gtk_action_set_sensitive( act, targetAction->sensitive );
696 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
698 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
699 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
701 return act;
702 }
704 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
705 {
706 Inkscape::UI::View::View *view = desktop;
707 gint verbsToUse[] = {
708 // disabled until we have icons for them:
709 //find
710 //SP_VERB_EDIT_TILE,
711 //SP_VERB_EDIT_UNTILE,
712 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
713 SP_VERB_DIALOG_DISPLAY,
714 SP_VERB_DIALOG_FILL_STROKE,
715 SP_VERB_DIALOG_NAMEDVIEW,
716 SP_VERB_DIALOG_TEXT,
717 SP_VERB_DIALOG_XML_EDITOR,
718 SP_VERB_EDIT_CLONE,
719 SP_VERB_EDIT_COPY,
720 SP_VERB_EDIT_CUT,
721 SP_VERB_EDIT_DUPLICATE,
722 SP_VERB_EDIT_PASTE,
723 SP_VERB_EDIT_REDO,
724 SP_VERB_EDIT_UNDO,
725 SP_VERB_EDIT_UNLINK_CLONE,
726 SP_VERB_FILE_EXPORT,
727 SP_VERB_FILE_IMPORT,
728 SP_VERB_FILE_NEW,
729 SP_VERB_FILE_OPEN,
730 SP_VERB_FILE_PRINT,
731 SP_VERB_FILE_SAVE,
732 SP_VERB_OBJECT_TO_CURVE,
733 SP_VERB_SELECTION_GROUP,
734 SP_VERB_SELECTION_OUTLINE,
735 SP_VERB_SELECTION_UNGROUP,
736 SP_VERB_ZOOM_1_1,
737 SP_VERB_ZOOM_1_2,
738 SP_VERB_ZOOM_2_1,
739 SP_VERB_ZOOM_DRAWING,
740 SP_VERB_ZOOM_IN,
741 SP_VERB_ZOOM_NEXT,
742 SP_VERB_ZOOM_OUT,
743 SP_VERB_ZOOM_PAGE,
744 SP_VERB_ZOOM_PAGE_WIDTH,
745 SP_VERB_ZOOM_PREV,
746 SP_VERB_ZOOM_SELECTION,
747 };
749 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
751 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
752 Glib::RefPtr<Gtk::ActionGroup> mainActions;
753 if ( groups.find(desktop) != groups.end() ) {
754 mainActions = groups[desktop];
755 }
757 if ( !mainActions ) {
758 mainActions = Gtk::ActionGroup::create("main");
759 groups[desktop] = mainActions;
760 }
762 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
763 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
764 if ( verb ) {
765 if (!mainActions->get_action(verb->get_id())) {
766 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
767 mainActions->add(Glib::wrap(act));
768 }
769 }
770 }
772 if ( !mainActions->get_action("ToolZoom") ) {
773 GtkTooltips *tt = gtk_tooltips_new();
774 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
775 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
776 if ( va ) {
777 mainActions->add(va);
778 if ( i == 0 ) {
779 va->set_active(true);
780 }
781 }
782 }
783 }
786 return mainActions;
787 }
790 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
791 {
792 gtk_widget_set_size_request( widget,
793 widget->allocation.width,
794 widget->allocation.height );
795 }
797 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
798 {
799 gtk_widget_set_size_request( widget, -1, -1 );
800 }
804 GtkWidget *
805 sp_tool_toolbox_new()
806 {
807 GtkTooltips *tt = gtk_tooltips_new();
808 GtkWidget* tb = gtk_toolbar_new();
809 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
810 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
812 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
813 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
815 gtk_widget_set_sensitive(tb, FALSE);
817 GtkWidget *hb = gtk_handle_box_new();
818 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
819 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
820 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
822 gtk_container_add(GTK_CONTAINER(hb), tb);
823 gtk_widget_show(GTK_WIDGET(tb));
825 sigc::connection* conn = new sigc::connection;
826 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
828 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
829 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
831 return hb;
832 }
834 GtkWidget *
835 sp_aux_toolbox_new()
836 {
837 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
839 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
841 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
843 gtk_widget_set_sensitive(tb, FALSE);
845 GtkWidget *hb = gtk_handle_box_new();
846 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
847 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
848 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
850 gtk_container_add(GTK_CONTAINER(hb), tb);
851 gtk_widget_show(GTK_WIDGET(tb));
853 sigc::connection* conn = new sigc::connection;
854 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
856 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
857 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
859 return hb;
860 }
862 //####################################
863 //# Commands Bar
864 //####################################
866 GtkWidget *
867 sp_commands_toolbox_new()
868 {
869 GtkWidget *tb = gtk_toolbar_new();
871 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
872 gtk_widget_set_sensitive(tb, FALSE);
874 GtkWidget *hb = gtk_handle_box_new();
875 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
876 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
877 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
879 gtk_container_add(GTK_CONTAINER(hb), tb);
880 gtk_widget_show(GTK_WIDGET(tb));
882 sigc::connection* conn = new sigc::connection;
883 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
885 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
886 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
888 return hb;
889 }
892 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
893 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
894 gchar const *path, gchar const *data, gdouble def,
895 GtkWidget *focusTarget,
896 GtkWidget *us,
897 GObject *dataKludge,
898 gboolean altx, gchar const *altx_mark,
899 gdouble lower, gdouble upper, gdouble step, gdouble page,
900 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
901 void (*callback)(GtkAdjustment *, GObject *),
902 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
903 {
904 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
905 lower, upper, step, page, page ) );
906 if (us) {
907 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
908 }
910 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
912 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
913 if ( shortLabel ) {
914 g_object_set( act, "short_label", shortLabel, NULL );
915 }
917 if ( (descrCount > 0) && descrLabels && descrValues ) {
918 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
919 }
921 if ( focusTarget ) {
922 ege_adjustment_action_set_focuswidget( act, focusTarget );
923 }
925 if ( altx && altx_mark ) {
926 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
927 }
929 if ( dataKludge ) {
930 g_object_set_data( dataKludge, data, adj );
931 }
933 // Using a cast just to make sure we pass in the right kind of function pointer
934 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
936 return act;
937 }
940 //####################################
941 //# node editing callbacks
942 //####################################
944 /**
945 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
946 */
947 static ShapeEditor *get_current_shape_editor()
948 {
949 if (!SP_ACTIVE_DESKTOP) {
950 return NULL;
951 }
953 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
955 if (!SP_IS_NODE_CONTEXT(event_context)) {
956 return NULL;
957 }
959 return SP_NODE_CONTEXT(event_context)->shape_editor;
960 }
963 void
964 sp_node_path_edit_add(void)
965 {
966 ShapeEditor *shape_editor = get_current_shape_editor();
967 if (shape_editor) shape_editor->add_node();
968 }
970 void
971 sp_node_path_edit_delete(void)
972 {
973 ShapeEditor *shape_editor = get_current_shape_editor();
974 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
975 }
977 void
978 sp_node_path_edit_delete_segment(void)
979 {
980 ShapeEditor *shape_editor = get_current_shape_editor();
981 if (shape_editor) shape_editor->delete_segment();
982 }
984 void
985 sp_node_path_edit_break(void)
986 {
987 ShapeEditor *shape_editor = get_current_shape_editor();
988 if (shape_editor) shape_editor->break_at_nodes();
989 }
991 void
992 sp_node_path_edit_join(void)
993 {
994 ShapeEditor *shape_editor = get_current_shape_editor();
995 if (shape_editor) shape_editor->join_nodes();
996 }
998 void
999 sp_node_path_edit_join_segment(void)
1000 {
1001 ShapeEditor *shape_editor = get_current_shape_editor();
1002 if (shape_editor) shape_editor->join_segments();
1003 }
1005 void
1006 sp_node_path_edit_toline(void)
1007 {
1008 ShapeEditor *shape_editor = get_current_shape_editor();
1009 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1010 }
1012 void
1013 sp_node_path_edit_tocurve(void)
1014 {
1015 ShapeEditor *shape_editor = get_current_shape_editor();
1016 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1017 }
1019 void
1020 sp_node_path_edit_cusp(void)
1021 {
1022 ShapeEditor *shape_editor = get_current_shape_editor();
1023 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1024 }
1026 void
1027 sp_node_path_edit_smooth(void)
1028 {
1029 ShapeEditor *shape_editor = get_current_shape_editor();
1030 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1031 }
1033 void
1034 sp_node_path_edit_symmetrical(void)
1035 {
1036 ShapeEditor *shape_editor = get_current_shape_editor();
1037 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1038 }
1040 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1041 bool show = gtk_toggle_action_get_active( act );
1042 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1043 ShapeEditor *shape_editor = get_current_shape_editor();
1044 if (shape_editor) shape_editor->show_handles(show);
1045 }
1047 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1048 bool show = gtk_toggle_action_get_active( act );
1049 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1050 ShapeEditor *shape_editor = get_current_shape_editor();
1051 if (shape_editor) shape_editor->show_helperpath(show);
1052 }
1054 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1055 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1056 }
1058 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1059 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1060 }
1062 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1063 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1064 }
1066 /* is called when the node selection is modified */
1067 static void
1068 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1069 {
1070 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1071 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1072 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1073 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1075 // quit if run by the attr_changed listener
1076 if (g_object_get_data( tbl, "freeze" )) {
1077 return;
1078 }
1080 // in turn, prevent listener from responding
1081 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1083 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1084 SPUnit const *unit = tracker->getActiveUnit();
1086 ShapeEditor *shape_editor = get_current_shape_editor();
1087 if (shape_editor && shape_editor->has_nodepath()) {
1088 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1089 int n_selected = 0;
1090 if (nodepath) {
1091 n_selected = nodepath->numSelected();
1092 }
1094 if (n_selected == 0) {
1095 gtk_action_set_sensitive(xact, FALSE);
1096 gtk_action_set_sensitive(yact, FALSE);
1097 } else {
1098 gtk_action_set_sensitive(xact, TRUE);
1099 gtk_action_set_sensitive(yact, TRUE);
1100 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1101 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1103 if (n_selected == 1) {
1104 NR::Point sel_node = nodepath->singleSelectedCoords();
1105 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1106 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1107 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1108 }
1109 } else {
1110 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1111 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1112 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1113 /* Note: Currently x and y will always have a value, even if the coordinates of the
1114 selected nodes don't coincide (in this case we use the coordinates of the center
1115 of the bounding box). So the entries are never set to zero. */
1116 // FIXME: Maybe we should clear the entry if several nodes are selected
1117 // instead of providing a kind of average value
1118 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1119 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1120 }
1121 }
1122 }
1123 } else {
1124 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1125 gtk_action_set_sensitive(xact, FALSE);
1126 gtk_action_set_sensitive(yact, FALSE);
1127 }
1129 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1130 }
1132 static void
1133 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1134 {
1135 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1137 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1138 SPUnit const *unit = tracker->getActiveUnit();
1140 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1141 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1142 }
1144 // quit if run by the attr_changed listener
1145 if (g_object_get_data( tbl, "freeze" )) {
1146 return;
1147 }
1149 // in turn, prevent listener from responding
1150 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1152 ShapeEditor *shape_editor = get_current_shape_editor();
1153 if (shape_editor && shape_editor->has_nodepath()) {
1154 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1155 if (!strcmp(value_name, "x")) {
1156 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1157 }
1158 if (!strcmp(value_name, "y")) {
1159 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1160 }
1161 }
1163 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1164 }
1166 static void
1167 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1168 {
1169 sp_node_path_value_changed(adj, tbl, "x");
1170 }
1172 static void
1173 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1174 {
1175 sp_node_path_value_changed(adj, tbl, "y");
1176 }
1178 void
1179 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1180 {
1181 {
1182 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1183 SPItem *item = selection->singleItem();
1184 if (item && SP_IS_LPE_ITEM(item)) {
1185 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1186 gtk_action_set_sensitive(w, TRUE);
1187 } else {
1188 gtk_action_set_sensitive(w, FALSE);
1189 }
1190 } else {
1191 gtk_action_set_sensitive(w, FALSE);
1192 }
1193 }
1195 {
1196 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1197 SPItem *item = selection->singleItem();
1198 if (item && item->clip_ref && item->clip_ref->getObject()) {
1199 gtk_action_set_sensitive(w, TRUE);
1200 } else {
1201 gtk_action_set_sensitive(w, FALSE);
1202 }
1203 }
1205 {
1206 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1207 SPItem *item = selection->singleItem();
1208 if (item && item->mask_ref && item->mask_ref->getObject()) {
1209 gtk_action_set_sensitive(w, TRUE);
1210 } else {
1211 gtk_action_set_sensitive(w, FALSE);
1212 }
1213 }
1214 }
1216 void
1217 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1218 {
1219 sp_node_toolbox_sel_changed (selection, tbl);
1220 }
1224 //################################
1225 //## Node Editing Toolbox ##
1226 //################################
1228 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1229 {
1230 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1231 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1232 g_object_set_data( holder, "tracker", tracker );
1234 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1236 {
1237 InkAction* inky = ink_action_new( "NodeInsertAction",
1238 _("Insert node"),
1239 _("Insert new nodes into selected segments"),
1240 "node_insert",
1241 secondarySize );
1242 g_object_set( inky, "short_label", _("Insert"), NULL );
1243 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1244 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1245 }
1247 {
1248 InkAction* inky = ink_action_new( "NodeDeleteAction",
1249 _("Delete node"),
1250 _("Delete selected nodes"),
1251 "node_delete",
1252 secondarySize );
1253 g_object_set( inky, "short_label", _("Delete"), NULL );
1254 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1255 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1256 }
1258 {
1259 InkAction* inky = ink_action_new( "NodeJoinAction",
1260 _("Join endnodes"),
1261 _("Join selected endnodes"),
1262 "node_join",
1263 secondarySize );
1264 g_object_set( inky, "short_label", _("Join"), NULL );
1265 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1266 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1267 }
1269 {
1270 InkAction* inky = ink_action_new( "NodeBreakAction",
1271 _("Break nodes"),
1272 _("Break path at selected nodes"),
1273 "node_break",
1274 secondarySize );
1275 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1276 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1277 }
1280 {
1281 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1282 _("Join with segment"),
1283 _("Join selected endnodes with a new segment"),
1284 "node_join_segment",
1285 secondarySize );
1286 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1287 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1288 }
1290 {
1291 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1292 _("Delete segment"),
1293 _("Delete segment between two non-endpoint nodes"),
1294 "node_delete_segment",
1295 secondarySize );
1296 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1297 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1298 }
1300 {
1301 InkAction* inky = ink_action_new( "NodeCuspAction",
1302 _("Node Cusp"),
1303 _("Make selected nodes corner"),
1304 "node_cusp",
1305 secondarySize );
1306 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1307 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1308 }
1310 {
1311 InkAction* inky = ink_action_new( "NodeSmoothAction",
1312 _("Node Smooth"),
1313 _("Make selected nodes smooth"),
1314 "node_smooth",
1315 secondarySize );
1316 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1317 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1318 }
1320 {
1321 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1322 _("Node Symmetric"),
1323 _("Make selected nodes symmetric"),
1324 "node_symmetric",
1325 secondarySize );
1326 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1327 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328 }
1330 {
1331 InkAction* inky = ink_action_new( "NodeLineAction",
1332 _("Node Line"),
1333 _("Make selected segments lines"),
1334 "node_line",
1335 secondarySize );
1336 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1337 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338 }
1340 {
1341 InkAction* inky = ink_action_new( "NodeCurveAction",
1342 _("Node Curve"),
1343 _("Make selected segments curves"),
1344 "node_curve",
1345 secondarySize );
1346 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1347 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348 }
1350 {
1351 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1352 _("Show Handles"),
1353 _("Show the Bezier handles of selected nodes"),
1354 "nodes_show_handles",
1355 Inkscape::ICON_SIZE_DECORATION );
1356 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1357 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1358 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1359 }
1361 {
1362 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1363 _("Show Outline"),
1364 _("Show the outline of the path"),
1365 "nodes_show_helperpath",
1366 Inkscape::ICON_SIZE_DECORATION );
1367 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1368 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1369 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1370 }
1372 {
1373 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1374 _("Next path effect parameter"),
1375 _("Show next path effect parameter for editing"),
1376 "edit_next_parameter",
1377 Inkscape::ICON_SIZE_DECORATION );
1378 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1379 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1380 g_object_set_data( holder, "nodes_lpeedit", inky);
1381 }
1383 {
1384 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1385 _("Edit clipping path"),
1386 _("Edit the clipping path of the object"),
1387 "nodeedit-clippath",
1388 Inkscape::ICON_SIZE_DECORATION );
1389 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1390 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1391 g_object_set_data( holder, "nodes_clippathedit", inky);
1392 }
1394 {
1395 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1396 _("Edit mask path"),
1397 _("Edit the mask of the object"),
1398 "nodeedit-mask",
1399 Inkscape::ICON_SIZE_DECORATION );
1400 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1401 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402 g_object_set_data( holder, "nodes_maskedit", inky);
1403 }
1405 /* X coord of selected node(s) */
1406 {
1407 EgeAdjustmentAction* eact = 0;
1408 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1409 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1410 eact = create_adjustment_action( "NodeXAction",
1411 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1412 "tools.nodes", "Xcoord", 0,
1413 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1414 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1415 labels, values, G_N_ELEMENTS(labels),
1416 sp_node_path_x_value_changed );
1417 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1418 g_object_set_data( holder, "nodes_x_action", eact );
1419 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1420 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1421 }
1423 /* Y coord of selected node(s) */
1424 {
1425 EgeAdjustmentAction* eact = 0;
1426 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1427 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1428 eact = create_adjustment_action( "NodeYAction",
1429 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1430 "tools.nodes", "Ycoord", 0,
1431 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1432 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1433 labels, values, G_N_ELEMENTS(labels),
1434 sp_node_path_y_value_changed );
1435 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1436 g_object_set_data( holder, "nodes_y_action", eact );
1437 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1438 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1439 }
1441 // add the units menu
1442 {
1443 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1444 gtk_action_group_add_action( mainActions, act );
1445 }
1448 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1450 //watch selection
1451 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1453 sigc::connection *c_selection_changed =
1454 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1455 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1456 pool->add_connection ("selection-changed", c_selection_changed);
1458 sigc::connection *c_selection_modified =
1459 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1460 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1461 pool->add_connection ("selection-modified", c_selection_modified);
1463 sigc::connection *c_subselection_changed =
1464 new sigc::connection (desktop->connectToolSubselectionChanged
1465 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1466 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1468 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1470 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1471 } // end of sp_node_toolbox_prep()
1474 //########################
1475 //## Zoom Toolbox ##
1476 //########################
1478 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1479 {
1480 // no custom GtkAction setup needed
1481 } // end of sp_zoom_toolbox_prep()
1483 void
1484 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1485 {
1486 toolbox_set_desktop(toolbox,
1487 desktop,
1488 setup_tool_toolbox,
1489 update_tool_toolbox,
1490 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1491 "event_context_connection")));
1492 }
1495 void
1496 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1497 {
1498 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1499 desktop,
1500 setup_aux_toolbox,
1501 update_aux_toolbox,
1502 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1503 "event_context_connection")));
1504 }
1506 void
1507 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1508 {
1509 toolbox_set_desktop(toolbox,
1510 desktop,
1511 setup_commands_toolbox,
1512 update_commands_toolbox,
1513 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1514 "event_context_connection")));
1515 }
1517 static void
1518 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1519 {
1520 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1521 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1523 if (old_desktop) {
1524 GList *children, *iter;
1526 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1527 for ( iter = children ; iter ; iter = iter->next ) {
1528 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1529 }
1530 g_list_free(children);
1531 }
1533 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1535 if (desktop) {
1536 gtk_widget_set_sensitive(toolbox, TRUE);
1537 setup_func(toolbox, desktop);
1538 update_func(desktop, desktop->event_context, toolbox);
1539 *conn = desktop->connectEventContextChanged
1540 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1541 } else {
1542 gtk_widget_set_sensitive(toolbox, FALSE);
1543 }
1545 } // end of toolbox_set_desktop()
1548 static void
1549 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1550 {
1551 gchar const * descr =
1552 "<ui>"
1553 " <toolbar name='ToolToolbar'>"
1554 " <toolitem action='ToolSelector' />"
1555 " <toolitem action='ToolNode' />"
1556 " <toolitem action='ToolTweak' />"
1557 " <toolitem action='ToolZoom' />"
1558 " <toolitem action='ToolRect' />"
1559 " <toolitem action='Tool3DBox' />"
1560 " <toolitem action='ToolArc' />"
1561 " <toolitem action='ToolStar' />"
1562 " <toolitem action='ToolSpiral' />"
1563 " <toolitem action='ToolPencil' />"
1564 " <toolitem action='ToolPen' />"
1565 " <toolitem action='ToolCalligraphic' />"
1566 " <toolitem action='ToolEraser' />"
1567 " <toolitem action='ToolPaintBucket' />"
1568 " <toolitem action='ToolText' />"
1569 " <toolitem action='ToolConnector' />"
1570 " <toolitem action='ToolGradient' />"
1571 " <toolitem action='ToolDropper' />"
1572 " </toolbar>"
1573 "</ui>";
1574 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1575 GtkUIManager* mgr = gtk_ui_manager_new();
1576 GError* errVal = 0;
1578 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1579 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1581 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1582 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1583 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1584 }
1585 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1586 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1588 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1589 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1591 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1593 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1594 if ( child ) {
1595 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1596 }
1598 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1599 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1600 }
1603 static void
1604 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1605 {
1606 gchar const *const tname = ( eventcontext
1607 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1608 : NULL );
1609 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1611 for (int i = 0 ; tools[i].type_name ; i++ ) {
1612 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1613 if ( act ) {
1614 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1615 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1616 if ( verbAct ) {
1617 verbAct->set_active(setActive);
1618 }
1619 }
1620 }
1621 }
1623 static void
1624 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1625 {
1626 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1627 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1628 GtkUIManager* mgr = gtk_ui_manager_new();
1629 GError* errVal = 0;
1630 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1631 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1633 std::map<std::string, GtkWidget*> dataHolders;
1635 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1636 if ( aux_toolboxes[i].prep_func ) {
1637 // converted to GtkActions and UIManager
1639 GtkWidget* kludge = gtk_toolbar_new();
1640 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1641 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1642 dataHolders[aux_toolboxes[i].type_name] = kludge;
1643 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1644 } else {
1646 GtkWidget *sub_toolbox = 0;
1647 if (aux_toolboxes[i].create_func == NULL)
1648 sub_toolbox = sp_empty_toolbox_new(desktop);
1649 else {
1650 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1651 }
1653 gtk_size_group_add_widget( grouper, sub_toolbox );
1655 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1656 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1658 }
1659 }
1661 // Second pass to create toolbars *after* all GtkActions are created
1662 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1663 if ( aux_toolboxes[i].prep_func ) {
1664 // converted to GtkActions and UIManager
1666 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1668 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1669 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1671 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1672 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1673 g_free( tmp );
1674 tmp = 0;
1676 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1677 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1678 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1679 }
1680 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1683 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1685 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1686 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1687 swatch->setDesktop( desktop );
1688 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1689 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1690 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1691 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 );
1692 }
1694 gtk_widget_show_all( holder );
1695 sp_set_font_size_smaller( holder );
1697 gtk_size_group_add_widget( grouper, holder );
1699 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1700 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1701 }
1702 }
1704 g_object_unref( G_OBJECT(grouper) );
1705 }
1707 static void
1708 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1709 {
1710 gchar const *tname = ( eventcontext
1711 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1712 : NULL );
1713 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1714 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1715 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1716 gtk_widget_show_all(sub_toolbox);
1717 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1718 } else {
1719 gtk_widget_hide(sub_toolbox);
1720 }
1721 }
1722 }
1724 static void
1725 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1726 {
1727 gchar const * descr =
1728 "<ui>"
1729 " <toolbar name='CommandsToolbar'>"
1730 " <toolitem action='FileNew' />"
1731 " <toolitem action='FileOpen' />"
1732 " <toolitem action='FileSave' />"
1733 " <toolitem action='FilePrint' />"
1734 " <separator />"
1735 " <toolitem action='FileImport' />"
1736 " <toolitem action='FileExport' />"
1737 " <separator />"
1738 " <toolitem action='EditUndo' />"
1739 " <toolitem action='EditRedo' />"
1740 " <separator />"
1741 " <toolitem action='EditCopy' />"
1742 " <toolitem action='EditCut' />"
1743 " <toolitem action='EditPaste' />"
1744 " <separator />"
1745 " <toolitem action='ZoomSelection' />"
1746 " <toolitem action='ZoomDrawing' />"
1747 " <toolitem action='ZoomPage' />"
1748 " <separator />"
1749 " <toolitem action='EditDuplicate' />"
1750 " <toolitem action='EditClone' />"
1751 " <toolitem action='EditUnlinkClone' />"
1752 " <separator />"
1753 " <toolitem action='SelectionGroup' />"
1754 " <toolitem action='SelectionUnGroup' />"
1755 " <separator />"
1756 " <toolitem action='DialogFillStroke' />"
1757 " <toolitem action='DialogText' />"
1758 " <toolitem action='DialogXMLEditor' />"
1759 " <toolitem action='DialogAlignDistribute' />"
1760 " <separator />"
1761 " <toolitem action='DialogPreferences' />"
1762 " <toolitem action='DialogDocumentProperties' />"
1763 " </toolbar>"
1764 "</ui>";
1765 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1768 GtkUIManager* mgr = gtk_ui_manager_new();
1769 GError* errVal = 0;
1771 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1772 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1774 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1775 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1776 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1777 }
1779 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1780 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1782 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1783 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1786 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1788 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1789 if ( child ) {
1790 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1791 }
1793 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1794 }
1796 static void
1797 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1798 {
1799 }
1801 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1802 {
1803 gtk_widget_show(toolbox_toplevel);
1804 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1806 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1807 if (!shown_toolbox) {
1808 return;
1809 }
1810 gtk_widget_show(toolbox);
1812 gtk_widget_show_all(shown_toolbox);
1813 }
1815 static GtkWidget *
1816 sp_empty_toolbox_new(SPDesktop *desktop)
1817 {
1818 GtkWidget *tbl = gtk_toolbar_new();
1819 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1820 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1822 gtk_widget_show_all(tbl);
1823 sp_set_font_size_smaller (tbl);
1825 return tbl;
1826 }
1828 #define MODE_LABEL_WIDTH 70
1830 //########################
1831 //## Star ##
1832 //########################
1834 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1835 {
1836 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1838 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1839 // do not remember prefs if this call is initiated by an undo change, because undoing object
1840 // creation sets bogus values to its attributes before it is deleted
1841 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1842 }
1844 // quit if run by the attr_changed listener
1845 if (g_object_get_data( dataKludge, "freeze" )) {
1846 return;
1847 }
1849 // in turn, prevent listener from responding
1850 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1852 bool modmade = false;
1854 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1855 GSList const *items = selection->itemList();
1856 for (; items != NULL; items = items->next) {
1857 if (SP_IS_STAR((SPItem *) items->data)) {
1858 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1859 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1860 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1861 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1862 + M_PI / (gint)adj->value));
1863 SP_OBJECT((SPItem *) items->data)->updateRepr();
1864 modmade = true;
1865 }
1866 }
1867 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1868 _("Star: Change number of corners"));
1870 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1871 }
1873 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1874 {
1875 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1877 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1878 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1879 }
1881 // quit if run by the attr_changed listener
1882 if (g_object_get_data( dataKludge, "freeze" )) {
1883 return;
1884 }
1886 // in turn, prevent listener from responding
1887 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1889 bool modmade = false;
1890 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1891 GSList const *items = selection->itemList();
1892 for (; items != NULL; items = items->next) {
1893 if (SP_IS_STAR((SPItem *) items->data)) {
1894 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1896 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1897 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1898 if (r2 < r1) {
1899 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1900 } else {
1901 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1902 }
1904 SP_OBJECT((SPItem *) items->data)->updateRepr();
1905 modmade = true;
1906 }
1907 }
1909 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1910 _("Star: Change spoke ratio"));
1912 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1913 }
1915 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1916 {
1917 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1918 bool flat = ege_select_one_action_get_active( act ) == 0;
1920 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1921 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1922 flat ? "true" : "false" );
1923 }
1925 // quit if run by the attr_changed listener
1926 if (g_object_get_data( dataKludge, "freeze" )) {
1927 return;
1928 }
1930 // in turn, prevent listener from responding
1931 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1933 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1934 GSList const *items = selection->itemList();
1935 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1936 bool modmade = false;
1938 if ( prop_action ) {
1939 gtk_action_set_sensitive( prop_action, !flat );
1940 }
1942 for (; items != NULL; items = items->next) {
1943 if (SP_IS_STAR((SPItem *) items->data)) {
1944 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1945 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1946 SP_OBJECT((SPItem *) items->data)->updateRepr();
1947 modmade = true;
1948 }
1949 }
1951 if (modmade) {
1952 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1953 flat ? _("Make polygon") : _("Make star"));
1954 }
1956 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1957 }
1959 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1960 {
1961 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1963 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1964 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1965 }
1967 // quit if run by the attr_changed listener
1968 if (g_object_get_data( dataKludge, "freeze" )) {
1969 return;
1970 }
1972 // in turn, prevent listener from responding
1973 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1975 bool modmade = false;
1977 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1978 GSList const *items = selection->itemList();
1979 for (; items != NULL; items = items->next) {
1980 if (SP_IS_STAR((SPItem *) items->data)) {
1981 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1982 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1983 SP_OBJECT(items->data)->updateRepr();
1984 modmade = true;
1985 }
1986 }
1987 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1988 _("Star: Change rounding"));
1990 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1991 }
1993 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1994 {
1995 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1997 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1998 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1999 }
2001 // quit if run by the attr_changed listener
2002 if (g_object_get_data( dataKludge, "freeze" )) {
2003 return;
2004 }
2006 // in turn, prevent listener from responding
2007 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2009 bool modmade = false;
2011 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2012 GSList const *items = selection->itemList();
2013 for (; items != NULL; items = items->next) {
2014 if (SP_IS_STAR((SPItem *) items->data)) {
2015 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2016 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2017 SP_OBJECT(items->data)->updateRepr();
2018 modmade = true;
2019 }
2020 }
2021 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2022 _("Star: Change randomization"));
2024 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2025 }
2028 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2029 gchar const */*old_value*/, gchar const */*new_value*/,
2030 bool /*is_interactive*/, gpointer data)
2031 {
2032 GtkWidget *tbl = GTK_WIDGET(data);
2034 // quit if run by the _changed callbacks
2035 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2036 return;
2037 }
2039 // in turn, prevent callbacks from responding
2040 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2042 GtkAdjustment *adj = 0;
2044 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2045 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2047 if (!strcmp(name, "inkscape:randomized")) {
2048 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2049 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2050 } else if (!strcmp(name, "inkscape:rounded")) {
2051 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2052 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2053 } else if (!strcmp(name, "inkscape:flatsided")) {
2054 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2055 char const *flatsides = repr->attribute("inkscape:flatsided");
2056 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2057 if ( flatsides && !strcmp(flatsides,"false") ) {
2058 ege_select_one_action_set_active( flat_action, 1 );
2059 gtk_action_set_sensitive( prop_action, TRUE );
2060 } else {
2061 ege_select_one_action_set_active( flat_action, 0 );
2062 gtk_action_set_sensitive( prop_action, FALSE );
2063 }
2064 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2065 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2066 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2067 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2068 if (r2 < r1) {
2069 gtk_adjustment_set_value(adj, r2/r1);
2070 } else {
2071 gtk_adjustment_set_value(adj, r1/r2);
2072 }
2073 } else if (!strcmp(name, "sodipodi:sides")) {
2074 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2075 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2076 }
2078 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2079 }
2082 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2083 {
2084 NULL, /* child_added */
2085 NULL, /* child_removed */
2086 star_tb_event_attr_changed,
2087 NULL, /* content_changed */
2088 NULL /* order_changed */
2089 };
2092 /**
2093 * \param selection Should not be NULL.
2094 */
2095 static void
2096 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2097 {
2098 int n_selected = 0;
2099 Inkscape::XML::Node *repr = NULL;
2101 purge_repr_listener( tbl, tbl );
2103 for (GSList const *items = selection->itemList();
2104 items != NULL;
2105 items = items->next)
2106 {
2107 if (SP_IS_STAR((SPItem *) items->data)) {
2108 n_selected++;
2109 repr = SP_OBJECT_REPR((SPItem *) items->data);
2110 }
2111 }
2113 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2115 if (n_selected == 0) {
2116 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2117 } else if (n_selected == 1) {
2118 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2120 if (repr) {
2121 g_object_set_data( tbl, "repr", repr );
2122 Inkscape::GC::anchor(repr);
2123 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2124 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2125 }
2126 } else {
2127 // FIXME: implement averaging of all parameters for multiple selected stars
2128 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2129 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2130 }
2131 }
2134 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2135 {
2136 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2137 // callbacks to lump all the changes for all selected objects in one undo step
2139 GtkAdjustment *adj = 0;
2141 // fixme: make settable in prefs!
2142 gint mag = 5;
2143 gdouble prop = 0.5;
2144 gboolean flat = FALSE;
2145 gdouble randomized = 0;
2146 gdouble rounded = 0;
2148 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2149 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2151 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2152 gtk_action_set_sensitive( sb2, !flat );
2154 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2155 gtk_adjustment_set_value(adj, mag);
2156 gtk_adjustment_value_changed(adj);
2158 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2159 gtk_adjustment_set_value(adj, prop);
2160 gtk_adjustment_value_changed(adj);
2162 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2163 gtk_adjustment_set_value(adj, rounded);
2164 gtk_adjustment_value_changed(adj);
2166 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2167 gtk_adjustment_set_value(adj, randomized);
2168 gtk_adjustment_value_changed(adj);
2169 }
2172 void
2173 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2174 {
2175 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2176 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2177 GtkWidget *l = gtk_label_new(NULL);
2178 gtk_label_set_markup(GTK_LABEL(l), title);
2179 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2180 if ( GTK_IS_TOOLBAR(tbl) ) {
2181 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2182 } else {
2183 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2184 }
2185 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2186 }
2189 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2190 {
2191 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2193 {
2194 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2195 ege_output_action_set_use_markup( act, TRUE );
2196 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2197 g_object_set_data( holder, "mode_action", act );
2198 }
2200 {
2201 EgeAdjustmentAction* eact = 0;
2202 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2203 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2205 /* Flatsided checkbox */
2206 {
2207 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2209 GtkTreeIter iter;
2210 gtk_list_store_append( model, &iter );
2211 gtk_list_store_set( model, &iter,
2212 0, _("Polygon"),
2213 1, _("Regular polygon (with one handle) instead of a star"),
2214 2, "star_flat",
2215 -1 );
2217 gtk_list_store_append( model, &iter );
2218 gtk_list_store_set( model, &iter,
2219 0, _("Star"),
2220 1, _("Star instead of a regular polygon (with one handle)"),
2221 2, "star_angled",
2222 -1 );
2224 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2225 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2226 g_object_set_data( holder, "flat_action", act );
2228 ege_select_one_action_set_appearance( act, "full" );
2229 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2230 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2231 ege_select_one_action_set_icon_column( act, 2 );
2232 ege_select_one_action_set_icon_size( act, secondarySize );
2233 ege_select_one_action_set_tooltip_column( act, 1 );
2235 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2236 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2237 }
2239 /* Magnitude */
2240 {
2241 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2242 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2243 eact = create_adjustment_action( "MagnitudeAction",
2244 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2245 "tools.shapes.star", "magnitude", 3,
2246 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2247 3, 1024, 1, 5,
2248 labels, values, G_N_ELEMENTS(labels),
2249 sp_stb_magnitude_value_changed,
2250 1.0, 0 );
2251 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2252 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2253 }
2255 /* Spoke ratio */
2256 {
2257 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2258 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2259 eact = create_adjustment_action( "SpokeAction",
2260 _("Spoke ratio"), _("Spoke ratio:"),
2261 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2262 // Base radius is the same for the closest handle.
2263 _("Base radius to tip radius ratio"),
2264 "tools.shapes.star", "proportion", 0.5,
2265 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2266 0.01, 1.0, 0.01, 0.1,
2267 labels, values, G_N_ELEMENTS(labels),
2268 sp_stb_proportion_value_changed );
2269 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2270 g_object_set_data( holder, "prop_action", eact );
2271 }
2273 if ( !isFlatSided ) {
2274 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2275 } else {
2276 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2277 }
2279 /* Roundedness */
2280 {
2281 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2282 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2283 eact = create_adjustment_action( "RoundednessAction",
2284 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2285 "tools.shapes.star", "rounded", 0.0,
2286 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2287 -10.0, 10.0, 0.01, 0.1,
2288 labels, values, G_N_ELEMENTS(labels),
2289 sp_stb_rounded_value_changed );
2290 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2291 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2292 }
2294 /* Randomization */
2295 {
2296 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2297 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2298 eact = create_adjustment_action( "RandomizationAction",
2299 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2300 "tools.shapes.star", "randomized", 0.0,
2301 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2302 -10.0, 10.0, 0.001, 0.01,
2303 labels, values, G_N_ELEMENTS(labels),
2304 sp_stb_randomized_value_changed, 0.1, 3 );
2305 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2306 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2307 }
2308 }
2310 {
2311 /* Reset */
2312 {
2313 GtkAction* act = gtk_action_new( "StarResetAction",
2314 _("Defaults"),
2315 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2316 GTK_STOCK_CLEAR );
2317 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2318 gtk_action_group_add_action( mainActions, act );
2319 gtk_action_set_sensitive( act, TRUE );
2320 }
2321 }
2323 sigc::connection *connection = new sigc::connection(
2324 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2325 );
2326 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2327 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2328 }
2331 //########################
2332 //## Rect ##
2333 //########################
2335 static void sp_rtb_sensitivize( GObject *tbl )
2336 {
2337 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2338 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2339 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2341 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2342 gtk_action_set_sensitive( not_rounded, FALSE );
2343 } else {
2344 gtk_action_set_sensitive( not_rounded, TRUE );
2345 }
2346 }
2349 static void
2350 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2351 void (*setter)(SPRect *, gdouble))
2352 {
2353 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2355 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2356 SPUnit const *unit = tracker->getActiveUnit();
2358 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2359 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2360 }
2362 // quit if run by the attr_changed listener
2363 if (g_object_get_data( tbl, "freeze" )) {
2364 return;
2365 }
2367 // in turn, prevent listener from responding
2368 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2370 bool modmade = false;
2371 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2372 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2373 if (SP_IS_RECT(items->data)) {
2374 if (adj->value != 0) {
2375 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2376 } else {
2377 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2378 }
2379 modmade = true;
2380 }
2381 }
2383 sp_rtb_sensitivize( tbl );
2385 if (modmade) {
2386 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2387 _("Change rectangle"));
2388 }
2390 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2391 }
2393 static void
2394 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2395 {
2396 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2397 }
2399 static void
2400 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2401 {
2402 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2403 }
2405 static void
2406 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2407 {
2408 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2409 }
2411 static void
2412 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2413 {
2414 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2415 }
2419 static void
2420 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2421 {
2422 GtkAdjustment *adj = 0;
2424 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2425 gtk_adjustment_set_value(adj, 0.0);
2426 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2427 gtk_adjustment_value_changed(adj);
2429 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2430 gtk_adjustment_set_value(adj, 0.0);
2431 gtk_adjustment_value_changed(adj);
2433 sp_rtb_sensitivize( obj );
2434 }
2436 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2437 gchar const */*old_value*/, gchar const */*new_value*/,
2438 bool /*is_interactive*/, gpointer data)
2439 {
2440 GObject *tbl = G_OBJECT(data);
2442 // quit if run by the _changed callbacks
2443 if (g_object_get_data( tbl, "freeze" )) {
2444 return;
2445 }
2447 // in turn, prevent callbacks from responding
2448 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2450 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2451 SPUnit const *unit = tracker->getActiveUnit();
2453 gpointer item = g_object_get_data( tbl, "item" );
2454 if (item && SP_IS_RECT(item)) {
2455 {
2456 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2457 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2458 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2459 }
2461 {
2462 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2463 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2464 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2465 }
2467 {
2468 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2469 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2470 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2471 }
2473 {
2474 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2475 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2476 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2477 }
2478 }
2480 sp_rtb_sensitivize( tbl );
2482 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2483 }
2486 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2487 NULL, /* child_added */
2488 NULL, /* child_removed */
2489 rect_tb_event_attr_changed,
2490 NULL, /* content_changed */
2491 NULL /* order_changed */
2492 };
2494 /**
2495 * \param selection should not be NULL.
2496 */
2497 static void
2498 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2499 {
2500 int n_selected = 0;
2501 Inkscape::XML::Node *repr = NULL;
2502 SPItem *item = NULL;
2504 if ( g_object_get_data( tbl, "repr" ) ) {
2505 g_object_set_data( tbl, "item", NULL );
2506 }
2507 purge_repr_listener( tbl, tbl );
2509 for (GSList const *items = selection->itemList();
2510 items != NULL;
2511 items = items->next) {
2512 if (SP_IS_RECT((SPItem *) items->data)) {
2513 n_selected++;
2514 item = (SPItem *) items->data;
2515 repr = SP_OBJECT_REPR(item);
2516 }
2517 }
2519 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2521 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2523 if (n_selected == 0) {
2524 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2526 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2527 gtk_action_set_sensitive(w, FALSE);
2528 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2529 gtk_action_set_sensitive(h, FALSE);
2531 } else if (n_selected == 1) {
2532 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2533 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2535 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2536 gtk_action_set_sensitive(w, TRUE);
2537 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2538 gtk_action_set_sensitive(h, TRUE);
2540 if (repr) {
2541 g_object_set_data( tbl, "repr", repr );
2542 g_object_set_data( tbl, "item", item );
2543 Inkscape::GC::anchor(repr);
2544 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2545 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2546 }
2547 } else {
2548 // FIXME: implement averaging of all parameters for multiple selected
2549 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2550 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2551 sp_rtb_sensitivize( tbl );
2552 }
2553 }
2556 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2557 {
2558 EgeAdjustmentAction* eact = 0;
2559 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2561 {
2562 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2563 ege_output_action_set_use_markup( act, TRUE );
2564 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2565 g_object_set_data( holder, "mode_action", act );
2566 }
2568 // rx/ry units menu: create
2569 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2570 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2571 // fixme: add % meaning per cent of the width/height
2572 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2573 g_object_set_data( holder, "tracker", tracker );
2575 /* W */
2576 {
2577 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2578 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2579 eact = create_adjustment_action( "RectWidthAction",
2580 _("Width"), _("W:"), _("Width of rectangle"),
2581 "tools.shapes.rect", "width", 0,
2582 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2583 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2584 labels, values, G_N_ELEMENTS(labels),
2585 sp_rtb_width_value_changed );
2586 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2587 g_object_set_data( holder, "width_action", eact );
2588 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2589 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2590 }
2592 /* H */
2593 {
2594 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2595 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2596 eact = create_adjustment_action( "RectHeightAction",
2597 _("Height"), _("H:"), _("Height of rectangle"),
2598 "tools.shapes.rect", "height", 0,
2599 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2600 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2601 labels, values, G_N_ELEMENTS(labels),
2602 sp_rtb_height_value_changed );
2603 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2604 g_object_set_data( holder, "height_action", eact );
2605 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2606 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2607 }
2609 /* rx */
2610 {
2611 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2612 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2613 eact = create_adjustment_action( "RadiusXAction",
2614 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2615 "tools.shapes.rect", "rx", 0,
2616 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2617 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2618 labels, values, G_N_ELEMENTS(labels),
2619 sp_rtb_rx_value_changed);
2620 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2621 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2622 }
2624 /* ry */
2625 {
2626 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2627 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2628 eact = create_adjustment_action( "RadiusYAction",
2629 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2630 "tools.shapes.rect", "ry", 0,
2631 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2632 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2633 labels, values, G_N_ELEMENTS(labels),
2634 sp_rtb_ry_value_changed);
2635 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2636 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2637 }
2639 // add the units menu
2640 {
2641 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2642 gtk_action_group_add_action( mainActions, act );
2643 }
2645 /* Reset */
2646 {
2647 InkAction* inky = ink_action_new( "RectResetAction",
2648 _("Not rounded"),
2649 _("Make corners sharp"),
2650 "squared_corner",
2651 secondarySize );
2652 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2653 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2654 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2655 g_object_set_data( holder, "not_rounded", inky );
2656 }
2658 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2659 sp_rtb_sensitivize( holder );
2661 sigc::connection *connection = new sigc::connection(
2662 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2663 );
2664 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2665 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2666 }
2668 //########################
2669 //## 3D Box ##
2670 //########################
2672 // normalize angle so that it lies in the interval [0,360]
2673 static double box3d_normalize_angle (double a) {
2674 double angle = a + ((int) (a/360.0))*360;
2675 if (angle < 0) {
2676 angle += 360.0;
2677 }
2678 return angle;
2679 }
2681 static void
2682 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2683 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2684 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2685 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2686 // are reset).
2687 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2689 if (is_infinite) {
2690 gtk_toggle_action_set_active(tact, TRUE);
2691 gtk_action_set_sensitive(act, TRUE);
2693 double angle = persp3d_get_infinite_angle(persp, axis);
2694 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2695 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2696 }
2697 } else {
2698 gtk_toggle_action_set_active(tact, FALSE);
2699 gtk_action_set_sensitive(act, FALSE);
2700 }
2701 }
2703 static void
2704 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2705 if (!persp_repr) {
2706 g_print ("No perspective given to box3d_resync_toolbar().\n");
2707 return;
2708 }
2710 GtkWidget *tbl = GTK_WIDGET(data);
2711 GtkAdjustment *adj = 0;
2712 GtkAction *act = 0;
2713 GtkToggleAction *tact = 0;
2714 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2715 {
2716 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2717 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2718 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2720 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2721 }
2722 {
2723 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2724 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2725 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2727 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2728 }
2729 {
2730 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2731 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2732 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2734 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2735 }
2736 }
2738 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2739 gchar const */*old_value*/, gchar const */*new_value*/,
2740 bool /*is_interactive*/, gpointer data)
2741 {
2742 GtkWidget *tbl = GTK_WIDGET(data);
2744 // quit if run by the attr_changed listener
2745 // note: it used to work without the differently called freeze_ attributes (here and in
2746 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2747 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2748 return;
2749 }
2751 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2752 // sp_document_maybe_done() when the document is undo insensitive)
2753 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2755 // TODO: Only update the appropriate part of the toolbar
2756 // if (!strcmp(name, "inkscape:vp_z")) {
2757 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2758 // }
2760 Persp3D *persp = persp3d_get_from_repr(repr);
2761 persp3d_update_box_reprs(persp);
2763 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2764 }
2766 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2767 {
2768 NULL, /* child_added */
2769 NULL, /* child_removed */
2770 box3d_persp_tb_event_attr_changed,
2771 NULL, /* content_changed */
2772 NULL /* order_changed */
2773 };
2775 /**
2776 * \param selection Should not be NULL.
2777 */
2778 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2779 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2780 static void
2781 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2782 {
2783 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2784 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2785 // update the perspectives with infinite VPs and leave the other ones untouched).
2787 Inkscape::XML::Node *persp_repr = NULL;
2788 purge_repr_listener(tbl, tbl);
2790 SPItem *item = selection->singleItem();
2791 if (item && SP_IS_BOX3D(item)) {
2792 // FIXME: Also deal with multiple selected boxes
2793 SPBox3D *box = SP_BOX3D(item);
2794 Persp3D *persp = box3d_get_perspective(box);
2795 persp_repr = SP_OBJECT_REPR(persp);
2796 if (persp_repr) {
2797 g_object_set_data(tbl, "repr", persp_repr);
2798 Inkscape::GC::anchor(persp_repr);
2799 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2800 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2801 }
2803 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2804 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2806 box3d_resync_toolbar(persp_repr, tbl);
2807 }
2808 }
2810 static void
2811 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2812 {
2813 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2814 SPDocument *document = sp_desktop_document(desktop);
2816 // quit if run by the attr_changed listener
2817 // note: it used to work without the differently called freeze_ attributes (here and in
2818 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2819 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2820 return;
2821 }
2823 // in turn, prevent listener from responding
2824 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2826 //Persp3D *persp = document->current_persp3d;
2827 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2828 if (sel_persps.empty()) {
2829 // this can happen when the document is created; we silently ignore it
2830 return;
2831 }
2832 Persp3D *persp = sel_persps.front();
2834 persp->tmat.set_infinite_direction (axis, adj->value);
2835 SP_OBJECT(persp)->updateRepr();
2837 // TODO: use the correct axis here, too
2838 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2840 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2841 }
2844 static void
2845 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2846 {
2847 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2848 }
2850 static void
2851 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2852 {
2853 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2854 }
2856 static void
2857 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2858 {
2859 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2860 }
2863 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2864 {
2865 // TODO: Take all selected perspectives into account
2866 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2867 if (sel_persps.empty()) {
2868 // this can happen when the document is created; we silently ignore it
2869 return;
2870 }
2871 Persp3D *persp = sel_persps.front();
2873 bool set_infinite = gtk_toggle_action_get_active(act);
2874 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2875 }
2877 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2878 {
2879 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2880 }
2882 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2883 {
2884 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2885 }
2887 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2888 {
2889 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2890 }
2892 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2893 {
2894 EgeAdjustmentAction* eact = 0;
2895 SPDocument *document = sp_desktop_document (desktop);
2896 Persp3D *persp = document->current_persp3d;
2898 EgeAdjustmentAction* box3d_angle_x = 0;
2899 EgeAdjustmentAction* box3d_angle_y = 0;
2900 EgeAdjustmentAction* box3d_angle_z = 0;
2902 /* Angle X */
2903 {
2904 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2905 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2906 eact = create_adjustment_action( "3DBoxAngleXAction",
2907 _("Angle in X direction"), _("Angle X:"),
2908 // Translators: PL is short for 'perspective line'
2909 _("Angle of PLs in X direction"),
2910 "tools.shapes.3dbox", "box3d_angle_x", 30,
2911 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2912 -360.0, 360.0, 1.0, 10.0,
2913 labels, values, G_N_ELEMENTS(labels),
2914 box3d_angle_x_value_changed );
2915 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2916 g_object_set_data( holder, "box3d_angle_x_action", eact );
2917 box3d_angle_x = eact;
2918 }
2920 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2921 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2922 } else {
2923 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2924 }
2927 /* VP X state */
2928 {
2929 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2930 // Translators: VP is short for 'vanishing point'
2931 _("State of VP in X direction"),
2932 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2933 "toggle_vp_x",
2934 Inkscape::ICON_SIZE_DECORATION );
2935 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2936 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2937 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2938 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2939 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2940 }
2942 /* Angle Y */
2943 {
2944 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2945 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2946 eact = create_adjustment_action( "3DBoxAngleYAction",
2947 _("Angle in Y direction"), _("Angle Y:"),
2948 // Translators: PL is short for 'perspective line'
2949 _("Angle of PLs in Y direction"),
2950 "tools.shapes.3dbox", "box3d_angle_y", 30,
2951 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2952 -360.0, 360.0, 1.0, 10.0,
2953 labels, values, G_N_ELEMENTS(labels),
2954 box3d_angle_y_value_changed );
2955 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2956 g_object_set_data( holder, "box3d_angle_y_action", eact );
2957 box3d_angle_y = eact;
2958 }
2960 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2961 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2962 } else {
2963 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2964 }
2966 /* VP Y state */
2967 {
2968 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2969 // Translators: VP is short for 'vanishing point'
2970 _("State of VP in Y direction"),
2971 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2972 "toggle_vp_y",
2973 Inkscape::ICON_SIZE_DECORATION );
2974 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2975 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2976 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2977 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2978 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2979 }
2981 /* Angle Z */
2982 {
2983 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2984 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2985 eact = create_adjustment_action( "3DBoxAngleZAction",
2986 _("Angle in Z direction"), _("Angle Z:"),
2987 // Translators: PL is short for 'perspective line'
2988 _("Angle of PLs in Z direction"),
2989 "tools.shapes.3dbox", "box3d_angle_z", 30,
2990 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2991 -360.0, 360.0, 1.0, 10.0,
2992 labels, values, G_N_ELEMENTS(labels),
2993 box3d_angle_z_value_changed );
2994 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2995 g_object_set_data( holder, "box3d_angle_z_action", eact );
2996 box3d_angle_z = eact;
2997 }
2999 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3000 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3001 } else {
3002 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3003 }
3005 /* VP Z state */
3006 {
3007 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3008 // Translators: VP is short for 'vanishing point'
3009 _("State of VP in Z direction"),
3010 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3011 "toggle_vp_z",
3012 Inkscape::ICON_SIZE_DECORATION );
3013 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3014 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3015 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3016 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3017 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3018 }
3020 sigc::connection *connection = new sigc::connection(
3021 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3022 );
3023 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3024 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3025 }
3027 //########################
3028 //## Spiral ##
3029 //########################
3031 static void
3032 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3033 {
3034 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3036 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3037 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3038 }
3040 // quit if run by the attr_changed listener
3041 if (g_object_get_data( tbl, "freeze" )) {
3042 return;
3043 }
3045 // in turn, prevent listener from responding
3046 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3048 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3050 bool modmade = false;
3051 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3052 items != NULL;
3053 items = items->next)
3054 {
3055 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3056 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3057 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3058 SP_OBJECT((SPItem *) items->data)->updateRepr();
3059 modmade = true;
3060 }
3061 }
3063 g_free(namespaced_name);
3065 if (modmade) {
3066 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3067 _("Change spiral"));
3068 }
3070 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3071 }
3073 static void
3074 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3075 {
3076 sp_spl_tb_value_changed(adj, tbl, "revolution");
3077 }
3079 static void
3080 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3081 {
3082 sp_spl_tb_value_changed(adj, tbl, "expansion");
3083 }
3085 static void
3086 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3087 {
3088 sp_spl_tb_value_changed(adj, tbl, "t0");
3089 }
3091 static void
3092 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3093 {
3094 GtkWidget *tbl = GTK_WIDGET(obj);
3096 GtkAdjustment *adj;
3098 // fixme: make settable
3099 gdouble rev = 5;
3100 gdouble exp = 1.0;
3101 gdouble t0 = 0.0;
3103 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3104 gtk_adjustment_set_value(adj, rev);
3105 gtk_adjustment_value_changed(adj);
3107 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3108 gtk_adjustment_set_value(adj, exp);
3109 gtk_adjustment_value_changed(adj);
3111 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3112 gtk_adjustment_set_value(adj, t0);
3113 gtk_adjustment_value_changed(adj);
3115 spinbutton_defocus(GTK_OBJECT(tbl));
3116 }
3119 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3120 gchar const */*old_value*/, gchar const */*new_value*/,
3121 bool /*is_interactive*/, gpointer data)
3122 {
3123 GtkWidget *tbl = GTK_WIDGET(data);
3125 // quit if run by the _changed callbacks
3126 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3127 return;
3128 }
3130 // in turn, prevent callbacks from responding
3131 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3133 GtkAdjustment *adj;
3134 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3135 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3137 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3138 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3140 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3141 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3143 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3144 }
3147 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3148 NULL, /* child_added */
3149 NULL, /* child_removed */
3150 spiral_tb_event_attr_changed,
3151 NULL, /* content_changed */
3152 NULL /* order_changed */
3153 };
3155 static void
3156 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3157 {
3158 int n_selected = 0;
3159 Inkscape::XML::Node *repr = NULL;
3161 purge_repr_listener( tbl, tbl );
3163 for (GSList const *items = selection->itemList();
3164 items != NULL;
3165 items = items->next)
3166 {
3167 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3168 n_selected++;
3169 repr = SP_OBJECT_REPR((SPItem *) items->data);
3170 }
3171 }
3173 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3175 if (n_selected == 0) {
3176 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3177 } else if (n_selected == 1) {
3178 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3180 if (repr) {
3181 g_object_set_data( tbl, "repr", repr );
3182 Inkscape::GC::anchor(repr);
3183 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3184 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3185 }
3186 } else {
3187 // FIXME: implement averaging of all parameters for multiple selected
3188 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3189 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3190 }
3191 }
3194 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3195 {
3196 EgeAdjustmentAction* eact = 0;
3197 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3199 {
3200 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3201 ege_output_action_set_use_markup( act, TRUE );
3202 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3203 g_object_set_data( holder, "mode_action", act );
3204 }
3206 /* Revolution */
3207 {
3208 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3209 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3210 eact = create_adjustment_action( "SpiralRevolutionAction",
3211 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3212 "tools.shapes.spiral", "revolution", 3.0,
3213 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3214 0.01, 1024.0, 0.1, 1.0,
3215 labels, values, G_N_ELEMENTS(labels),
3216 sp_spl_tb_revolution_value_changed, 1, 2);
3217 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3218 }
3220 /* Expansion */
3221 {
3222 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3223 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3224 eact = create_adjustment_action( "SpiralExpansionAction",
3225 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3226 "tools.shapes.spiral", "expansion", 1.0,
3227 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3228 0.0, 1000.0, 0.01, 1.0,
3229 labels, values, G_N_ELEMENTS(labels),
3230 sp_spl_tb_expansion_value_changed);
3231 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3232 }
3234 /* T0 */
3235 {
3236 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3237 gdouble values[] = {0, 0.5, 0.9};
3238 eact = create_adjustment_action( "SpiralT0Action",
3239 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3240 "tools.shapes.spiral", "t0", 0.0,
3241 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3242 0.0, 0.999, 0.01, 1.0,
3243 labels, values, G_N_ELEMENTS(labels),
3244 sp_spl_tb_t0_value_changed);
3245 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3246 }
3248 /* Reset */
3249 {
3250 InkAction* inky = ink_action_new( "SpiralResetAction",
3251 _("Defaults"),
3252 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3253 GTK_STOCK_CLEAR,
3254 secondarySize );
3255 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3256 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3257 }
3260 sigc::connection *connection = new sigc::connection(
3261 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3262 );
3263 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3264 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3265 }
3267 //########################
3268 //## Pen/Pencil ##
3269 //########################
3271 static char const *
3272 freehand_tool_name(GObject *dataKludge)
3273 {
3274 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3275 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3276 ? "tools.freehand.pen"
3277 : "tools.freehand.pencil" );
3278 }
3280 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3281 {
3282 prefs_set_int_attribute(freehand_tool_name(tbl), "spiro-spline-mode", ege_select_one_action_get_active(act));
3283 }
3285 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3286 {
3287 /* Freehand mode toggle buttons */
3288 {
3289 // FIXME: spiroMode seems not to be read correctly here during startup, although the
3290 // correct mode is used in pen/pencil tool later on; same for freehand shapes
3291 guint spiroMode = prefs_get_int_attribute(freehand_tool_name(holder), "spiro-spline-mode", 0);
3292 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3294 {
3295 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3297 GtkTreeIter iter;
3298 gtk_list_store_append( model, &iter );
3299 gtk_list_store_set( model, &iter,
3300 0, _("Bézier"),
3301 1, _("Regular Bézier mode"),
3302 2, "bezier_mode",
3303 -1 );
3305 gtk_list_store_append( model, &iter );
3306 gtk_list_store_set( model, &iter,
3307 0, _("Spiro"),
3308 1, _("Spiro splines mode"),
3309 2, "spiro_splines_mode",
3310 -1 );
3312 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3313 "FreehandModeActionPencil" :
3314 "FreehandModeActionPen",
3315 ("Mode:"), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3316 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3318 ege_select_one_action_set_appearance( act, "full" );
3319 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3320 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3321 ege_select_one_action_set_icon_column( act, 2 );
3322 ege_select_one_action_set_icon_size( act, secondarySize );
3323 ege_select_one_action_set_tooltip_column( act, 1 );
3325 ege_select_one_action_set_active( act, spiroMode);
3326 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3327 }
3328 }
3329 }
3331 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3332 gint shape = ege_select_one_action_get_active( act );
3333 prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3334 }
3336 /**
3337 * \brief Generate the list of freehand advanced shape option entries.
3338 */
3339 GList * freehand_shape_dropdown_items_list() {
3340 GList *glist = NULL;
3342 glist = g_list_append (glist, _("None"));
3343 glist = g_list_append (glist, _("Clipboard"));
3344 glist = g_list_append (glist, _("Crescendo"));
3345 glist = g_list_append (glist, _("Decrescendo"));
3347 return glist;
3348 }
3350 static void
3351 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3352 /*advanced shape options */
3353 {
3354 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3356 GList* items = 0;
3357 gint count = 0;
3358 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3359 {
3360 GtkTreeIter iter;
3361 gtk_list_store_append( model, &iter );
3362 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3363 count++;
3364 }
3365 g_list_free( items );
3366 items = 0;
3367 EgeSelectOneAction* act1 = ege_select_one_action_new(
3368 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3369 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3370 g_object_set( act1, "short_label", _("Shape:"), NULL );
3371 ege_select_one_action_set_appearance( act1, "compact" );
3372 ege_select_one_action_set_active( act1, prefs_get_int_attribute(freehand_tool_name(holder), "shape", 0) );
3373 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3374 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3375 g_object_set_data( holder, "shape_action", act1 );
3376 }
3377 }
3379 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3380 {
3381 sp_add_spiro_toggle(mainActions, holder, false);
3382 sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3383 }
3386 static void
3387 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3388 {
3389 GtkWidget *tbl = GTK_WIDGET(obj);
3391 GtkAdjustment *adj;
3393 // fixme: make settable
3394 gdouble tolerance = 4;
3396 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3397 gtk_adjustment_set_value(adj, tolerance);
3398 gtk_adjustment_value_changed(adj);
3400 spinbutton_defocus(GTK_OBJECT(tbl));
3401 }
3403 static void
3404 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3405 {
3407 // quit if run by the attr_changed listener
3408 if (g_object_get_data( tbl, "freeze" )) {
3409 return;
3410 }
3411 // in turn, prevent listener from responding
3412 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3413 prefs_set_double_attribute("tools.freehand.pencil",
3414 "tolerance", adj->value);
3415 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3417 }
3421 static void
3422 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3423 const gchar */*key*/,
3424 const gchar */*oldval*/,
3425 const gchar */*newval*/,
3426 bool /*is_interactive*/,
3427 void * data)
3428 {
3429 GObject* tbl = G_OBJECT(data);
3430 if (g_object_get_data( tbl, "freeze" )) {
3431 return;
3432 }
3434 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3436 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3437 "tolerance");
3439 double v = prefs_get_double_attribute("tools.freehand.pencil",
3440 "tolerance", adj->value);
3441 gtk_adjustment_set_value(adj, v);
3442 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3444 }
3446 static Inkscape::XML::NodeEventVector pencil_node_events =
3447 {
3448 NULL,
3449 NULL,
3450 sp_pencil_tb_tolerance_value_changed_external,
3451 NULL,
3452 NULL,
3453 };
3456 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3457 {
3458 sp_add_spiro_toggle(mainActions, holder, true);
3460 EgeAdjustmentAction* eact = 0;
3462 /* Tolerance */
3463 {
3464 gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3465 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3466 eact = create_adjustment_action( "PencilToleranceAction",
3467 _("Smoothing:"), _("Smoothing"),
3468 _("How much smoothing (simplifying) is applied to the line"),
3469 "tools.freehand.pencil", "tolerance",
3470 3.0,
3471 GTK_WIDGET(desktop->canvas), NULL,
3472 holder, TRUE, "altx-pencil",
3473 1, 100.0, 0.5, 0,
3474 labels, values, G_N_ELEMENTS(labels),
3475 sp_pencil_tb_tolerance_value_changed,
3476 1, 2);
3477 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3478 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3480 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3481 "tools.freehand.pencil");
3482 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3483 g_object_set_data(G_OBJECT(holder), "repr", repr);
3485 }
3487 /* advanced shape options */
3488 sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3490 /* Reset */
3491 {
3492 InkAction* inky = ink_action_new( "PencilResetAction",
3493 _("Defaults"),
3494 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3495 GTK_STOCK_CLEAR,
3496 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3497 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3498 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3499 }
3501 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3503 }
3506 //########################
3507 //## Tweak ##
3508 //########################
3510 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3511 {
3512 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3513 }
3515 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3516 {
3517 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3518 }
3520 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3521 {
3522 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3523 }
3525 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3526 {
3527 int mode = ege_select_one_action_get_active( act );
3528 prefs_set_int_attribute("tools.tweak", "mode", mode);
3530 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3531 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3532 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3533 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3534 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3535 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3536 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3537 if (doh) gtk_action_set_sensitive (doh, TRUE);
3538 if (dos) gtk_action_set_sensitive (dos, TRUE);
3539 if (dol) gtk_action_set_sensitive (dol, TRUE);
3540 if (doo) gtk_action_set_sensitive (doo, TRUE);
3541 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3542 if (fid) gtk_action_set_sensitive (fid, FALSE);
3543 } else {
3544 if (doh) gtk_action_set_sensitive (doh, FALSE);
3545 if (dos) gtk_action_set_sensitive (dos, FALSE);
3546 if (dol) gtk_action_set_sensitive (dol, FALSE);
3547 if (doo) gtk_action_set_sensitive (doo, FALSE);
3548 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3549 if (fid) gtk_action_set_sensitive (fid, TRUE);
3550 }
3551 }
3553 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3554 {
3555 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3556 }
3558 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3559 bool show = gtk_toggle_action_get_active( act );
3560 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3561 }
3562 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3563 bool show = gtk_toggle_action_get_active( act );
3564 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3565 }
3566 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3567 bool show = gtk_toggle_action_get_active( act );
3568 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3569 }
3570 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3571 bool show = gtk_toggle_action_get_active( act );
3572 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3573 }
3575 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3576 {
3577 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3579 {
3580 /* Width */
3581 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3582 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3583 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3584 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3585 "tools.tweak", "width", 15,
3586 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3587 1, 100, 1.0, 0.0,
3588 labels, values, G_N_ELEMENTS(labels),
3589 sp_tweak_width_value_changed, 0.01, 0, 100 );
3590 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3591 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3592 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3593 }
3596 {
3597 /* Force */
3598 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3599 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3600 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3601 _("Force"), _("Force:"), _("The force of the tweak action"),
3602 "tools.tweak", "force", 20,
3603 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3604 1, 100, 1.0, 0.0,
3605 labels, values, G_N_ELEMENTS(labels),
3606 sp_tweak_force_value_changed, 0.01, 0, 100 );
3607 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3608 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3609 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3610 }
3612 /* Mode */
3613 {
3614 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3616 GtkTreeIter iter;
3617 gtk_list_store_append( model, &iter );
3618 gtk_list_store_set( model, &iter,
3619 0, _("Push mode"),
3620 1, _("Push parts of paths in any direction"),
3621 2, "tweak_push_mode",
3622 -1 );
3624 gtk_list_store_append( model, &iter );
3625 gtk_list_store_set( model, &iter,
3626 0, _("Shrink mode"),
3627 1, _("Shrink (inset) parts of paths"),
3628 2, "tweak_shrink_mode",
3629 -1 );
3631 gtk_list_store_append( model, &iter );
3632 gtk_list_store_set( model, &iter,
3633 0, _("Grow mode"),
3634 1, _("Grow (outset) parts of paths"),
3635 2, "tweak_grow_mode",
3636 -1 );
3638 gtk_list_store_append( model, &iter );
3639 gtk_list_store_set( model, &iter,
3640 0, _("Attract mode"),
3641 1, _("Attract parts of paths towards cursor"),
3642 2, "tweak_attract_mode",
3643 -1 );
3645 gtk_list_store_append( model, &iter );
3646 gtk_list_store_set( model, &iter,
3647 0, _("Repel mode"),
3648 1, _("Repel parts of paths from cursor"),
3649 2, "tweak_repel_mode",
3650 -1 );
3652 gtk_list_store_append( model, &iter );
3653 gtk_list_store_set( model, &iter,
3654 0, _("Roughen mode"),
3655 1, _("Roughen parts of paths"),
3656 2, "tweak_roughen_mode",
3657 -1 );
3659 gtk_list_store_append( model, &iter );
3660 gtk_list_store_set( model, &iter,
3661 0, _("Color paint mode"),
3662 1, _("Paint the tool's color upon selected objects"),
3663 2, "tweak_colorpaint_mode",
3664 -1 );
3666 gtk_list_store_append( model, &iter );
3667 gtk_list_store_set( model, &iter,
3668 0, _("Color jitter mode"),
3669 1, _("Jitter the colors of selected objects"),
3670 2, "tweak_colorjitter_mode",
3671 -1 );
3673 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3674 g_object_set( act, "short_label", _("Mode:"), NULL );
3675 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3676 g_object_set_data( holder, "mode_action", act );
3678 ege_select_one_action_set_appearance( act, "full" );
3679 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3680 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3681 ege_select_one_action_set_icon_column( act, 2 );
3682 ege_select_one_action_set_icon_size( act, secondarySize );
3683 ege_select_one_action_set_tooltip_column( act, 1 );
3685 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3686 ege_select_one_action_set_active( act, mode );
3687 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3689 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3690 }
3692 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3694 {
3695 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3696 ege_output_action_set_use_markup( act, TRUE );
3697 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3698 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3699 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3700 g_object_set_data( holder, "tweak_channels_label", act);
3701 }
3703 {
3704 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3705 _("Hue"),
3706 _("In color mode, act on objects' hue"),
3707 NULL,
3708 Inkscape::ICON_SIZE_DECORATION );
3709 //TRANSLATORS: "H" here stands for hue
3710 g_object_set( act, "short_label", _("H"), NULL );
3711 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3712 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3713 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3714 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3715 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3716 g_object_set_data( holder, "tweak_doh", act);
3717 }
3718 {
3719 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3720 _("Saturation"),
3721 _("In color mode, act on objects' saturation"),
3722 NULL,
3723 Inkscape::ICON_SIZE_DECORATION );
3724 //TRANSLATORS: "S" here stands for Saturation
3725 g_object_set( act, "short_label", _("S"), NULL );
3726 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3727 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3728 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3729 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3730 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3731 g_object_set_data( holder, "tweak_dos", act );
3732 }
3733 {
3734 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3735 _("Lightness"),
3736 _("In color mode, act on objects' lightness"),
3737 NULL,
3738 Inkscape::ICON_SIZE_DECORATION );
3739 //TRANSLATORS: "L" here stands for Lightness
3740 g_object_set( act, "short_label", _("L"), NULL );
3741 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3742 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3743 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3744 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3745 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3746 g_object_set_data( holder, "tweak_dol", act );
3747 }
3748 {
3749 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3750 _("Opacity"),
3751 _("In color mode, act on objects' opacity"),
3752 NULL,
3753 Inkscape::ICON_SIZE_DECORATION );
3754 //TRANSLATORS: "O" here stands for Opacity
3755 g_object_set( act, "short_label", _("O"), NULL );
3756 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3757 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3758 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3759 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3760 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3761 g_object_set_data( holder, "tweak_doo", act );
3762 }
3764 { /* Fidelity */
3765 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3766 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3767 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3768 _("Fidelity"), _("Fidelity:"),
3769 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3770 "tools.tweak", "fidelity", 50,
3771 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3772 1, 100, 1.0, 10.0,
3773 labels, values, G_N_ELEMENTS(labels),
3774 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3775 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3776 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3777 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3778 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3779 g_object_set_data( holder, "tweak_fidelity", eact );
3780 }
3783 /* Use Pressure button */
3784 {
3785 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3786 _("Pressure"),
3787 _("Use the pressure of the input device to alter the force of tweak action"),
3788 "use_pressure",
3789 Inkscape::ICON_SIZE_DECORATION );
3790 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3791 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3792 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3793 }
3795 }
3798 //########################
3799 //## Calligraphy ##
3800 //########################
3801 static void update_presets_list (GObject *tbl){
3803 if (g_object_get_data(tbl, "presets_blocked"))
3804 return;
3806 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3807 if (sel) {
3808 ege_select_one_action_set_active(sel, 0);
3809 }
3810 }
3812 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3813 {
3814 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3815 update_presets_list(tbl);
3816 }
3818 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3819 {
3820 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3821 update_presets_list(tbl);
3822 }
3824 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3825 {
3826 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3827 update_presets_list(tbl);
3828 }
3830 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3831 {
3832 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3833 update_presets_list(tbl);
3834 }
3836 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3837 {
3838 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3839 update_presets_list(tbl);
3840 }
3842 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3843 {
3844 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3845 update_presets_list(tbl);
3846 }
3848 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3849 {
3850 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3851 update_presets_list(tbl);
3852 }
3854 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3855 {
3856 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3857 update_presets_list(tbl);
3858 }
3860 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3861 {
3862 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3863 update_presets_list(tbl);
3864 }
3866 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3867 {
3868 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3869 update_presets_list(tbl);
3870 }
3872 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3873 {
3874 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3875 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3876 update_presets_list(tbl);
3877 if (calligraphy_angle )
3878 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3879 }
3882 #define NUMBER_OF_PRESET_PARAMS 11
3883 static gchar * widget_names[NUMBER_OF_PRESET_PARAMS] = {
3884 "width",
3885 "mass",
3886 "wiggle",
3887 "angle",
3888 "thinning",
3889 "tremor",
3890 "flatness",
3891 "cap_rounding",
3892 "usepressure",
3893 "tracebackground",
3894 "usetilt"
3895 };
3898 static void sp_dcc_build_presets_list(GObject *tbl)
3899 {
3900 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3901 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3902 gtk_list_store_clear (model);
3904 {
3905 GtkTreeIter iter;
3906 gtk_list_store_append( model, &iter );
3907 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3908 }
3910 //TODO: switch back to prefs API
3911 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3912 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3913 int ii=1;
3914 while (child_repr) {
3915 GtkTreeIter iter;
3916 char *preset_name = (char *) child_repr->attribute("name");
3917 gtk_list_store_append( model, &iter );
3918 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3919 child_repr = sp_repr_next(child_repr);
3920 }
3922 {
3923 GtkTreeIter iter;
3924 gtk_list_store_append( model, &iter );
3925 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
3926 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
3927 }
3929 update_presets_list (tbl);
3930 }
3932 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
3933 {
3934 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
3935 if (! desktop) return;
3937 if (g_object_get_data(tbl, "presets_blocked"))
3938 return;
3940 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3941 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
3942 // dialog cancelled
3943 update_presets_list (tbl);
3944 return;
3945 }
3946 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3948 if (!profile_name.c_str() || *profile_name.c_str() == 0) {
3949 // empty name entered
3950 update_presets_list (tbl);
3951 return;
3952 }
3954 int new_index = -1;
3955 gchar *pref_path = NULL;
3956 int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3958 for (int i = 1; i <= total_prefs; i++) {
3959 gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
3960 const gchar *name = prefs_get_string_attribute(path, "name");
3961 if (name && !strcmp(name, profile_name.c_str())) {
3962 // we already have preset with this name, replace its values
3963 new_index = i;
3964 pref_path = g_strdup(path);
3965 break;
3966 }
3967 }
3969 if (new_index == -1) {
3970 // no preset with this name, create
3971 new_index = total_prefs + 1;
3972 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3973 pref_path = create_pref("tools.calligraphic.preset", profile_id);
3974 free(profile_id);
3975 }
3977 for (unsigned i = 0; i < NUMBER_OF_PRESET_PARAMS; ++i) {
3978 gchar *widget_name = widget_names[i];
3979 void *widget = g_object_get_data(tbl, widget_name);
3980 if (widget) {
3981 if (GTK_IS_ADJUSTMENT(widget)) {
3982 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3983 double v = gtk_adjustment_get_value(adj);
3984 prefs_set_double_attribute(pref_path, widget_name, v);
3985 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
3986 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3987 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3988 int v = gtk_toggle_action_get_active(toggle);
3989 prefs_set_int_attribute(pref_path, widget_name, v);
3990 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
3991 } else {
3992 g_warning("Unknown widget type for preset: %s\n", widget_name);
3993 }
3994 } else {
3995 g_warning("Bad key when writing preset: %s\n", widget_name);
3996 }
3997 }
3998 prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4000 sp_dcc_build_presets_list (tbl);
4002 free (pref_path);
4003 }
4006 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4008 gint preset_index = ege_select_one_action_get_active( act );
4009 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4011 if (preset_index == save_presets_index) {
4012 // this is the Save command
4013 sp_dcc_save_profile(NULL, tbl);
4014 return;
4015 }
4017 gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4019 if (preset_path) {
4020 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
4022 Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4024 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4025 iter;
4026 ++iter ) {
4027 const gchar *attr_name = g_quark_to_string(iter->key);
4028 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4029 continue;
4030 void *widget = g_object_get_data(tbl, attr_name);
4031 if (widget) {
4032 if (GTK_IS_ADJUSTMENT(widget)) {
4033 double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4034 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4035 gtk_adjustment_set_value(adj, v);
4036 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4037 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4038 int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4039 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4040 gtk_toggle_action_set_active(toggle, v);
4041 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4042 } else {
4043 g_warning("Unknown widget type for preset: %s\n", attr_name);
4044 }
4045 } else {
4046 g_warning("Bad key found in a preset record: %s\n", attr_name);
4047 }
4048 }
4049 free(preset_path);
4050 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4051 }
4053 }
4056 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4057 {
4058 {
4059 EgeAdjustmentAction* calligraphy_angle = 0;
4061 {
4062 /* Width */
4063 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4064 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4065 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4066 _("Pen Width"), _("Width:"),
4067 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4068 "tools.calligraphic", "width", 15,
4069 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4070 1, 100, 1.0, 0.0,
4071 labels, values, G_N_ELEMENTS(labels),
4072 sp_ddc_width_value_changed, 0.01, 0, 100 );
4073 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4074 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4075 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4076 }
4078 {
4079 /* Thinning */
4080 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4081 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4082 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4083 _("Stroke Thinning"), _("Thinning:"),
4084 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4085 "tools.calligraphic", "thinning", 10,
4086 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4087 -100, 100, 1, 0.1,
4088 labels, values, G_N_ELEMENTS(labels),
4089 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4090 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4091 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4092 }
4094 {
4095 /* Angle */
4096 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4097 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4098 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4099 _("Pen Angle"), _("Angle:"),
4100 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4101 "tools.calligraphic", "angle", 30,
4102 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4103 -90.0, 90.0, 1.0, 10.0,
4104 labels, values, G_N_ELEMENTS(labels),
4105 sp_ddc_angle_value_changed, 1, 0 );
4106 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4107 g_object_set_data( holder, "angle_action", eact );
4108 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4109 calligraphy_angle = eact;
4110 }
4112 {
4113 /* Fixation */
4114 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4115 gdouble values[] = {0, 20, 40, 60, 90, 100};
4116 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4117 _("Fixation"), _("Fixation:"),
4118 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4119 "tools.calligraphic", "flatness", 90,
4120 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4121 0.0, 100, 1.0, 10.0,
4122 labels, values, G_N_ELEMENTS(labels),
4123 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4124 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4125 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4126 }
4128 {
4129 /* Cap Rounding */
4130 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4131 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4132 // TRANSLATORS: "cap" means "end" (both start and finish) here
4133 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4134 _("Cap rounding"), _("Caps:"),
4135 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4136 "tools.calligraphic", "cap_rounding", 0.0,
4137 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4138 0.0, 5.0, 0.01, 0.1,
4139 labels, values, G_N_ELEMENTS(labels),
4140 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4141 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4142 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4143 }
4145 {
4146 /* Tremor */
4147 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4148 gdouble values[] = {0, 10, 20, 40, 60, 100};
4149 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4150 _("Stroke Tremor"), _("Tremor:"),
4151 _("Increase to make strokes rugged and trembling"),
4152 "tools.calligraphic", "tremor", 0.0,
4153 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4154 0.0, 100, 1, 0.0,
4155 labels, values, G_N_ELEMENTS(labels),
4156 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4158 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4159 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4160 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4161 }
4163 {
4164 /* Wiggle */
4165 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4166 gdouble values[] = {0, 20, 40, 60, 100};
4167 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4168 _("Pen Wiggle"), _("Wiggle:"),
4169 _("Increase to make the pen waver and wiggle"),
4170 "tools.calligraphic", "wiggle", 0.0,
4171 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4172 0.0, 100, 1, 0.0,
4173 labels, values, G_N_ELEMENTS(labels),
4174 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4175 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4176 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4177 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4178 }
4180 {
4181 /* Mass */
4182 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4183 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4184 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4185 _("Pen Mass"), _("Mass:"),
4186 _("Increase to make the pen drag behind, as if slowed by inertia"),
4187 "tools.calligraphic", "mass", 2.0,
4188 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4189 0.0, 100, 1, 0.0,
4190 labels, values, G_N_ELEMENTS(labels),
4191 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4192 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4193 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4194 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4195 }
4198 /* Trace Background button */
4199 {
4200 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4201 _("Trace Background"),
4202 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4203 "trace_background",
4204 Inkscape::ICON_SIZE_DECORATION );
4205 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4206 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4207 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4208 g_object_set_data( holder, "tracebackground", act );
4209 }
4211 /* Use Pressure button */
4212 {
4213 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4214 _("Pressure"),
4215 _("Use the pressure of the input device to alter the width of the pen"),
4216 "use_pressure",
4217 Inkscape::ICON_SIZE_DECORATION );
4218 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4219 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4220 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4221 g_object_set_data( holder, "usepressure", act );
4222 }
4224 /* Use Tilt button */
4225 {
4226 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4227 _("Tilt"),
4228 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4229 "use_tilt",
4230 Inkscape::ICON_SIZE_DECORATION );
4231 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4232 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4233 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4234 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4235 g_object_set_data( holder, "usetilt", act );
4236 }
4238 /*calligraphic profile */
4239 {
4240 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4241 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model));
4242 ege_select_one_action_set_appearance (act1, "compact");
4243 g_object_set_data (holder, "profile_selector", act1 );
4245 sp_dcc_build_presets_list (holder);
4247 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4248 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4249 }
4250 }
4251 }
4254 //########################
4255 //## Circle / Arc ##
4256 //########################
4258 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4259 {
4260 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4261 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4263 if (v1 == 0 && v2 == 0) {
4264 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4265 gtk_action_set_sensitive( ocb, FALSE );
4266 gtk_action_set_sensitive( make_whole, FALSE );
4267 }
4268 } else {
4269 gtk_action_set_sensitive( ocb, TRUE );
4270 gtk_action_set_sensitive( make_whole, TRUE );
4271 }
4272 }
4274 static void
4275 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4276 {
4277 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4279 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4280 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4281 }
4283 // quit if run by the attr_changed listener
4284 if (g_object_get_data( tbl, "freeze" )) {
4285 return;
4286 }
4288 // in turn, prevent listener from responding
4289 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4291 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4293 bool modmade = false;
4294 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4295 items != NULL;
4296 items = items->next)
4297 {
4298 SPItem *item = SP_ITEM(items->data);
4300 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4302 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4303 SPArc *arc = SP_ARC(item);
4305 if (!strcmp(value_name, "start"))
4306 ge->start = (adj->value * M_PI)/ 180;
4307 else
4308 ge->end = (adj->value * M_PI)/ 180;
4310 sp_genericellipse_normalize(ge);
4311 ((SPObject *)arc)->updateRepr();
4312 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4314 modmade = true;
4315 }
4316 }
4318 g_free(namespaced_name);
4320 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4322 sp_arctb_sensitivize( tbl, adj->value, other->value );
4324 if (modmade) {
4325 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4326 _("Arc: Change start/end"));
4327 }
4329 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4330 }
4333 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4334 {
4335 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4336 }
4338 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4339 {
4340 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4341 }
4344 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4345 {
4346 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4347 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4348 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4349 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4350 }
4352 // only take action if run by the attr_changed listener
4353 if (!g_object_get_data( tbl, "freeze" )) {
4354 // in turn, prevent listener from responding
4355 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4357 if ( eraserMode != 0 ) {
4358 } else {
4359 }
4360 // TODO finish implementation
4362 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4363 }
4364 }
4366 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4367 {
4368 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4369 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4370 if ( ege_select_one_action_get_active( act ) != 0 ) {
4371 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4372 } else {
4373 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4374 }
4375 }
4377 // quit if run by the attr_changed listener
4378 if (g_object_get_data( tbl, "freeze" )) {
4379 return;
4380 }
4382 // in turn, prevent listener from responding
4383 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4385 bool modmade = false;
4387 if ( ege_select_one_action_get_active(act) != 0 ) {
4388 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4389 items != NULL;
4390 items = items->next)
4391 {
4392 if (SP_IS_ARC((SPItem *) items->data)) {
4393 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4394 repr->setAttribute("sodipodi:open", "true");
4395 SP_OBJECT((SPItem *) items->data)->updateRepr();
4396 modmade = true;
4397 }
4398 }
4399 } else {
4400 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4401 items != NULL;
4402 items = items->next)
4403 {
4404 if (SP_IS_ARC((SPItem *) items->data)) {
4405 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4406 repr->setAttribute("sodipodi:open", NULL);
4407 SP_OBJECT((SPItem *) items->data)->updateRepr();
4408 modmade = true;
4409 }
4410 }
4411 }
4413 if (modmade) {
4414 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4415 _("Arc: Change open/closed"));
4416 }
4418 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4419 }
4421 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4422 {
4423 GtkAdjustment *adj;
4424 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4425 gtk_adjustment_set_value(adj, 0.0);
4426 gtk_adjustment_value_changed(adj);
4428 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4429 gtk_adjustment_set_value(adj, 0.0);
4430 gtk_adjustment_value_changed(adj);
4432 spinbutton_defocus( GTK_OBJECT(obj) );
4433 }
4435 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4436 gchar const */*old_value*/, gchar const */*new_value*/,
4437 bool /*is_interactive*/, gpointer data)
4438 {
4439 GObject *tbl = G_OBJECT(data);
4441 // quit if run by the _changed callbacks
4442 if (g_object_get_data( tbl, "freeze" )) {
4443 return;
4444 }
4446 // in turn, prevent callbacks from responding
4447 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4449 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4450 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4452 GtkAdjustment *adj1,*adj2;
4453 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4454 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4455 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4456 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4458 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4460 char const *openstr = NULL;
4461 openstr = repr->attribute("sodipodi:open");
4462 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4464 if (openstr) {
4465 ege_select_one_action_set_active( ocb, 1 );
4466 } else {
4467 ege_select_one_action_set_active( ocb, 0 );
4468 }
4470 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4471 }
4473 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4474 NULL, /* child_added */
4475 NULL, /* child_removed */
4476 arc_tb_event_attr_changed,
4477 NULL, /* content_changed */
4478 NULL /* order_changed */
4479 };
4482 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4483 {
4484 int n_selected = 0;
4485 Inkscape::XML::Node *repr = NULL;
4487 purge_repr_listener( tbl, tbl );
4489 for (GSList const *items = selection->itemList();
4490 items != NULL;
4491 items = items->next)
4492 {
4493 if (SP_IS_ARC((SPItem *) items->data)) {
4494 n_selected++;
4495 repr = SP_OBJECT_REPR((SPItem *) items->data);
4496 }
4497 }
4499 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4501 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4502 if (n_selected == 0) {
4503 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4504 } else if (n_selected == 1) {
4505 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4506 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4508 if (repr) {
4509 g_object_set_data( tbl, "repr", repr );
4510 Inkscape::GC::anchor(repr);
4511 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4512 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4513 }
4514 } else {
4515 // FIXME: implement averaging of all parameters for multiple selected
4516 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4517 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4518 sp_arctb_sensitivize( tbl, 1, 0 );
4519 }
4520 }
4523 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4524 {
4525 EgeAdjustmentAction* eact = 0;
4526 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4529 {
4530 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4531 ege_output_action_set_use_markup( act, TRUE );
4532 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4533 g_object_set_data( holder, "mode_action", act );
4534 }
4536 /* Start */
4537 {
4538 eact = create_adjustment_action( "ArcStartAction",
4539 _("Start"), _("Start:"),
4540 _("The angle (in degrees) from the horizontal to the arc's start point"),
4541 "tools.shapes.arc", "start", 0.0,
4542 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4543 -360.0, 360.0, 1.0, 10.0,
4544 0, 0, 0,
4545 sp_arctb_start_value_changed);
4546 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4547 }
4549 /* End */
4550 {
4551 eact = create_adjustment_action( "ArcEndAction",
4552 _("End"), _("End:"),
4553 _("The angle (in degrees) from the horizontal to the arc's end point"),
4554 "tools.shapes.arc", "end", 0.0,
4555 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4556 -360.0, 360.0, 1.0, 10.0,
4557 0, 0, 0,
4558 sp_arctb_end_value_changed);
4559 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4560 }
4562 /* Segments / Pie checkbox */
4563 {
4564 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4566 GtkTreeIter iter;
4567 gtk_list_store_append( model, &iter );
4568 gtk_list_store_set( model, &iter,
4569 0, _("Closed arc"),
4570 1, _("Switch to segment (closed shape with two radii)"),
4571 2, "circle_closed_arc",
4572 -1 );
4574 gtk_list_store_append( model, &iter );
4575 gtk_list_store_set( model, &iter,
4576 0, _("Open Arc"),
4577 1, _("Switch to arc (unclosed shape)"),
4578 2, "circle_open_arc",
4579 -1 );
4581 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4582 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4583 g_object_set_data( holder, "open_action", act );
4585 ege_select_one_action_set_appearance( act, "full" );
4586 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4587 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4588 ege_select_one_action_set_icon_column( act, 2 );
4589 ege_select_one_action_set_icon_size( act, secondarySize );
4590 ege_select_one_action_set_tooltip_column( act, 1 );
4592 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4593 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4594 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4595 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4596 }
4598 /* Make Whole */
4599 {
4600 InkAction* inky = ink_action_new( "ArcResetAction",
4601 _("Make whole"),
4602 _("Make the shape a whole ellipse, not arc or segment"),
4603 "reset_circle",
4604 secondarySize );
4605 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4606 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4607 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4608 g_object_set_data( holder, "make_whole", inky );
4609 }
4611 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4612 // sensitivize make whole and open checkbox
4613 {
4614 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4615 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4616 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4617 }
4620 sigc::connection *connection = new sigc::connection(
4621 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4622 );
4623 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4624 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4625 }
4630 // toggle button callbacks and updaters
4632 //########################
4633 //## Dropper ##
4634 //########################
4636 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4637 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4638 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4639 if ( set_action ) {
4640 if ( gtk_toggle_action_get_active( act ) ) {
4641 gtk_action_set_sensitive( set_action, TRUE );
4642 } else {
4643 gtk_action_set_sensitive( set_action, FALSE );
4644 }
4645 }
4647 spinbutton_defocus(GTK_OBJECT(tbl));
4648 }
4650 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4651 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4652 spinbutton_defocus(GTK_OBJECT(tbl));
4653 }
4656 /**
4657 * Dropper auxiliary toolbar construction and setup.
4658 *
4659 * TODO: Would like to add swatch of current color.
4660 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4661 * can drag and drop places. Will provide a nice mixing palette.
4662 */
4663 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4664 {
4665 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4667 {
4668 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4669 ege_output_action_set_use_markup( act, TRUE );
4670 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4671 }
4673 {
4674 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4675 _("Pick opacity"),
4676 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4677 NULL,
4678 Inkscape::ICON_SIZE_DECORATION );
4679 g_object_set( act, "short_label", _("Pick"), NULL );
4680 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4681 g_object_set_data( holder, "pick_action", act );
4682 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4683 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4684 }
4686 {
4687 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4688 _("Assign opacity"),
4689 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4690 NULL,
4691 Inkscape::ICON_SIZE_DECORATION );
4692 g_object_set( act, "short_label", _("Assign"), NULL );
4693 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4694 g_object_set_data( holder, "set_action", act );
4695 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4696 // make sure it's disabled if we're not picking alpha
4697 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4698 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4699 }
4700 }
4704 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4705 {
4706 {
4707 /* Width */
4708 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4709 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4710 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4711 _("Pen Width"), _("Width:"),
4712 _("The width of the eraser pen (relative to the visible canvas area)"),
4713 "tools.eraser", "width", 15,
4714 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4715 1, 100, 1.0, 0.0,
4716 labels, values, G_N_ELEMENTS(labels),
4717 sp_ddc_width_value_changed, 0.01, 0, 100 );
4718 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4719 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4720 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4721 }
4723 {
4724 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4726 GtkTreeIter iter;
4727 gtk_list_store_append( model, &iter );
4728 gtk_list_store_set( model, &iter,
4729 0, _("Delete"),
4730 1, _("Delete objects touched by the eraser"),
4731 2, "delete_object",
4732 -1 );
4734 gtk_list_store_append( model, &iter );
4735 gtk_list_store_set( model, &iter,
4736 0, _("Cut"),
4737 1, _("Cut out from objects"),
4738 2, "difference",
4739 -1 );
4741 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4742 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4743 g_object_set_data( holder, "eraser_mode_action", act );
4745 ege_select_one_action_set_appearance( act, "full" );
4746 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4747 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4748 ege_select_one_action_set_icon_column( act, 2 );
4749 ege_select_one_action_set_tooltip_column( act, 1 );
4751 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4752 ege_select_one_action_set_active( act, eraserMode );
4753 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4754 }
4756 }
4758 //########################
4759 //## Text Toolbox ##
4760 //########################
4761 /*
4762 static void
4763 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4764 {
4765 //Call back for letter sizing spinbutton
4766 }
4768 static void
4769 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4770 {
4771 //Call back for line height spinbutton
4772 }
4774 static void
4775 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4776 {
4777 //Call back for horizontal kerning spinbutton
4778 }
4780 static void
4781 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4782 {
4783 //Call back for vertical kerning spinbutton
4784 }
4786 static void
4787 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4788 {
4789 //Call back for letter rotation spinbutton
4790 }*/
4792 namespace {
4794 bool popdown_visible = false;
4795 bool popdown_hasfocus = false;
4797 void
4798 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4799 {
4800 SPStyle *query =
4801 sp_style_new (SP_ACTIVE_DOCUMENT);
4803 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4805 int result_family =
4806 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4808 int result_style =
4809 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4811 int result_numbers =
4812 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4814 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4816 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4817 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
4818 // there are no texts in selection, read from prefs
4820 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4821 if (repr) {
4822 sp_style_read_from_repr (query, repr);
4823 if (g_object_get_data(tbl, "text_style_from_prefs")) {
4824 // do not reset the toolbar style from prefs if we already did it last time
4825 sp_style_unref(query);
4826 return;
4827 }
4828 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
4829 } else {
4830 sp_style_unref(query);
4831 return;
4832 }
4833 } else {
4834 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
4835 }
4837 if (query->text)
4838 {
4839 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4840 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4841 gtk_entry_set_text (GTK_ENTRY (entry), "");
4843 } else if (query->text->font_specification.value || query->text->font_family.value) {
4845 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4847 // Get the font that corresponds
4848 Glib::ustring familyName;
4850 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4851 if (font) {
4852 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4853 font->Unref();
4854 font = NULL;
4855 }
4857 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4859 Gtk::TreePath path;
4860 try {
4861 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4862 } catch (...) {
4863 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4864 sp_style_unref(query);
4865 return;
4866 }
4868 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4869 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4871 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4873 gtk_tree_selection_select_path (tselection, path.gobj());
4874 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4876 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4877 }
4879 //Size
4880 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4881 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4882 g_object_set_data (tbl, "size-block", gpointer(1));
4883 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4884 g_object_set_data (tbl, "size-block", gpointer(0));
4885 free (str);
4887 //Anchor
4888 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4889 {
4890 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4891 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4892 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4893 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4894 }
4895 else
4896 {
4897 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4898 {
4899 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4900 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4901 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4902 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4903 }
4904 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4905 {
4906 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4907 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4908 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4909 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4910 }
4911 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4912 {
4913 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4914 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4915 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4916 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4917 }
4918 }
4920 //Style
4921 {
4922 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4924 gboolean active = gtk_toggle_button_get_active (button);
4925 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4927 if (active != check)
4928 {
4929 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4930 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4931 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4932 }
4933 }
4935 {
4936 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4938 gboolean active = gtk_toggle_button_get_active (button);
4939 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4941 if (active != check)
4942 {
4943 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4944 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4945 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4946 }
4947 }
4949 //Orientation
4950 //locking both buttons, changing one affect all group (both)
4951 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4952 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4954 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4955 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4957 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4958 {
4959 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4960 }
4961 else
4962 {
4963 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4964 }
4965 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4966 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4967 }
4969 sp_style_unref(query);
4970 }
4972 void
4973 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4974 {
4975 sp_text_toolbox_selection_changed (selection, tbl);
4976 }
4978 void
4979 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4980 {
4981 sp_text_toolbox_selection_changed (NULL, tbl);
4982 }
4984 void
4985 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4986 GObject *tbl)
4987 {
4988 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4989 GtkTreeModel *model = 0;
4990 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4991 GtkTreeIter iter;
4992 char *family = 0;
4994 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4995 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4997 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4998 return;
4999 }
5001 gtk_tree_model_get (model, &iter, 0, &family, -1);
5003 if (g_object_get_data (G_OBJECT (selection), "block"))
5004 {
5005 gtk_entry_set_text (GTK_ENTRY (entry), family);
5006 return;
5007 }
5009 gtk_entry_set_text (GTK_ENTRY (entry), family);
5011 SPStyle *query =
5012 sp_style_new (SP_ACTIVE_DOCUMENT);
5014 int result_fontspec =
5015 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5017 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5019 SPCSSAttr *css = sp_repr_css_attr_new ();
5022 // First try to get the font spec from the stored value
5023 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5025 if (fontSpec.empty()) {
5026 // Construct a new font specification if it does not yet exist
5027 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5028 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5029 fontFromStyle->Unref();
5030 }
5032 if (!fontSpec.empty()) {
5033 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5034 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5035 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5036 if (font) {
5037 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5039 // Set all the these just in case they were altered when finding the best
5040 // match for the new family and old style...
5042 gchar c[256];
5044 font->Family(c, 256);
5045 sp_repr_css_set_property (css, "font-family", c);
5047 font->Attribute( "weight", c, 256);
5048 sp_repr_css_set_property (css, "font-weight", c);
5050 font->Attribute("style", c, 256);
5051 sp_repr_css_set_property (css, "font-style", c);
5053 font->Attribute("stretch", c, 256);
5054 sp_repr_css_set_property (css, "font-stretch", c);
5056 font->Attribute("variant", c, 256);
5057 sp_repr_css_set_property (css, "font-variant", c);
5059 font->Unref();
5060 }
5061 }
5062 }
5064 // If querying returned nothing, set the default style of the tool (for new texts)
5065 if (result_fontspec == QUERY_STYLE_NOTHING)
5066 {
5067 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5068 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5069 }
5070 else
5071 {
5072 sp_desktop_set_style (desktop, css, true, true);
5073 }
5075 sp_style_unref(query);
5077 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5078 _("Text: Change font family"));
5079 sp_repr_css_attr_unref (css);
5080 free (family);
5081 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5083 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5084 }
5086 void
5087 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5088 GObject *tbl)
5089 {
5090 const char *family = gtk_entry_get_text (entry);
5092 try {
5093 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5094 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5095 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5096 gtk_tree_selection_select_path (selection, path.gobj());
5097 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5098 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5099 } catch (...) {
5100 if (family && strlen (family))
5101 {
5102 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5103 }
5104 }
5105 }
5107 void
5108 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5109 gpointer data)
5110 {
5111 if (g_object_get_data (G_OBJECT (button), "block")) return;
5112 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5113 int prop = GPOINTER_TO_INT(data);
5115 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5116 SPCSSAttr *css = sp_repr_css_attr_new ();
5118 switch (prop)
5119 {
5120 case 0:
5121 {
5122 sp_repr_css_set_property (css, "text-anchor", "start");
5123 sp_repr_css_set_property (css, "text-align", "start");
5124 break;
5125 }
5126 case 1:
5127 {
5128 sp_repr_css_set_property (css, "text-anchor", "middle");
5129 sp_repr_css_set_property (css, "text-align", "center");
5130 break;
5131 }
5133 case 2:
5134 {
5135 sp_repr_css_set_property (css, "text-anchor", "end");
5136 sp_repr_css_set_property (css, "text-align", "end");
5137 break;
5138 }
5140 case 3:
5141 {
5142 sp_repr_css_set_property (css, "text-anchor", "start");
5143 sp_repr_css_set_property (css, "text-align", "justify");
5144 break;
5145 }
5146 }
5148 SPStyle *query =
5149 sp_style_new (SP_ACTIVE_DOCUMENT);
5150 int result_numbers =
5151 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5153 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5154 if (result_numbers == QUERY_STYLE_NOTHING)
5155 {
5156 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5157 }
5159 sp_style_unref(query);
5161 sp_desktop_set_style (desktop, css, true, true);
5162 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5163 _("Text: Change alignment"));
5164 sp_repr_css_attr_unref (css);
5166 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5167 }
5169 void
5170 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5171 gpointer data)
5172 {
5173 if (g_object_get_data (G_OBJECT (button), "block")) return;
5175 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5176 SPCSSAttr *css = sp_repr_css_attr_new ();
5177 int prop = GPOINTER_TO_INT(data);
5178 bool active = gtk_toggle_button_get_active (button);
5180 SPStyle *query =
5181 sp_style_new (SP_ACTIVE_DOCUMENT);
5183 int result_fontspec =
5184 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5186 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5187 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5188 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5190 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5191 Glib::ustring newFontSpec = "";
5193 if (fontSpec.empty()) {
5194 // Construct a new font specification if it does not yet exist
5195 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5196 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5197 fontFromStyle->Unref();
5198 }
5200 switch (prop)
5201 {
5202 case 0:
5203 {
5204 if (!fontSpec.empty()) {
5205 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5206 }
5207 if (fontSpec != newFontSpec) {
5208 // Don't even set the bold if the font didn't exist on the system
5209 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5210 }
5211 break;
5212 }
5214 case 1:
5215 {
5216 if (!fontSpec.empty()) {
5217 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5218 }
5219 if (fontSpec != newFontSpec) {
5220 // Don't even set the italic if the font didn't exist on the system
5221 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5222 }
5223 break;
5224 }
5225 }
5227 if (!newFontSpec.empty()) {
5228 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5229 }
5231 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5232 if (result_fontspec == QUERY_STYLE_NOTHING)
5233 {
5234 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5235 }
5237 sp_style_unref(query);
5239 sp_desktop_set_style (desktop, css, true, true);
5240 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5241 _("Text: Change font style"));
5242 sp_repr_css_attr_unref (css);
5244 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5245 }
5247 void
5248 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5249 gpointer data)
5250 {
5251 if (g_object_get_data (G_OBJECT (button), "block")) {
5252 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5253 return;
5254 }
5256 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5257 SPCSSAttr *css = sp_repr_css_attr_new ();
5258 int prop = GPOINTER_TO_INT(data);
5260 switch (prop)
5261 {
5262 case 0:
5263 {
5264 sp_repr_css_set_property (css, "writing-mode", "lr");
5265 break;
5266 }
5268 case 1:
5269 {
5270 sp_repr_css_set_property (css, "writing-mode", "tb");
5271 break;
5272 }
5273 }
5275 SPStyle *query =
5276 sp_style_new (SP_ACTIVE_DOCUMENT);
5277 int result_numbers =
5278 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5280 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5281 if (result_numbers == QUERY_STYLE_NOTHING)
5282 {
5283 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5284 }
5286 sp_desktop_set_style (desktop, css, true, true);
5287 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5288 _("Text: Change orientation"));
5289 sp_repr_css_attr_unref (css);
5291 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5292 }
5294 gboolean
5295 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5296 {
5297 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5298 if (!desktop) return FALSE;
5300 switch (get_group0_keyval (event)) {
5301 case GDK_Escape: // defocus
5302 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5303 sp_text_toolbox_selection_changed (NULL, tbl); // update
5304 return TRUE; // I consumed the event
5305 break;
5306 }
5307 return FALSE;
5308 }
5310 gboolean
5311 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5312 {
5313 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5314 if (!desktop) return FALSE;
5316 switch (get_group0_keyval (event)) {
5317 case GDK_KP_Enter:
5318 case GDK_Return:
5319 case GDK_Escape: // defocus
5320 gtk_widget_hide (w);
5321 popdown_visible = false;
5322 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5323 return TRUE; // I consumed the event
5324 break;
5325 case GDK_w:
5326 case GDK_W:
5327 if (event->state & GDK_CONTROL_MASK) {
5328 gtk_widget_hide (w);
5329 popdown_visible = false;
5330 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5331 return TRUE; // I consumed the event
5332 }
5333 break;
5334 }
5335 return FALSE;
5336 }
5339 void
5340 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5341 GObject *tbl)
5342 {
5343 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5345 if (g_object_get_data (tbl, "size-block")) return;
5347 // If this is not from selecting a size in the list (in which case get_active will give the
5348 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5349 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5350 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5351 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5352 return;
5354 gchar *endptr;
5355 gdouble value = -1;
5356 char *text = gtk_combo_box_get_active_text (cbox);
5357 if (text) {
5358 value = g_strtod (text, &endptr);
5359 if (endptr == text) // conversion failed, non-numeric input
5360 value = -1;
5361 free (text);
5362 }
5363 if (value <= 0) {
5364 return; // could not parse value
5365 }
5367 SPCSSAttr *css = sp_repr_css_attr_new ();
5368 Inkscape::CSSOStringStream osfs;
5369 osfs << value;
5370 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5372 SPStyle *query =
5373 sp_style_new (SP_ACTIVE_DOCUMENT);
5374 int result_numbers =
5375 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5377 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5378 if (result_numbers == QUERY_STYLE_NOTHING)
5379 {
5380 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5381 }
5383 sp_style_unref(query);
5385 sp_desktop_set_style (desktop, css, true, true);
5386 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5387 _("Text: Change font size"));
5388 sp_repr_css_attr_unref (css);
5390 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5391 }
5393 gboolean
5394 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5395 {
5396 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5397 if (!desktop) return FALSE;
5399 if (!g_object_get_data (tbl, "esc-pressed")) {
5400 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5401 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5402 sp_text_toolbox_size_changed (cbox, tbl);
5403 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5404 }
5405 return FALSE; // I consumed the event
5406 }
5409 gboolean
5410 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5411 {
5412 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5413 if (!desktop) return FALSE;
5415 switch (get_group0_keyval (event)) {
5416 case GDK_Escape: // defocus
5417 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5418 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5419 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5420 return TRUE; // I consumed the event
5421 break;
5422 case GDK_Return: // defocus
5423 case GDK_KP_Enter:
5424 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5425 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5426 sp_text_toolbox_size_changed (cbox, tbl);
5427 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5428 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5429 return TRUE; // I consumed the event
5430 break;
5431 }
5432 return FALSE;
5433 }
5435 void
5436 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5437 GObject *tbl)
5438 {
5439 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5440 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5441 int x, y;
5443 if (!popdown_visible)
5444 {
5445 gdk_window_get_origin (widget->window, &x, &y);
5446 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5447 gtk_widget_show_all (popdown);
5448 //sp_transientize (popdown);
5450 gdk_pointer_grab (widget->window, TRUE,
5451 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5452 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5453 GDK_POINTER_MOTION_MASK),
5454 NULL, NULL, GDK_CURRENT_TIME);
5456 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5458 popdown_visible = true;
5459 }
5460 else
5461 {
5462 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5463 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5464 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5465 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5466 gtk_widget_hide (popdown);
5467 popdown_visible = false;
5468 }
5469 }
5471 gboolean
5472 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5473 GdkEventFocus */*event*/,
5474 GObject */*tbl*/)
5475 {
5476 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5477 return FALSE;
5478 }
5480 gboolean
5481 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5482 GdkEventFocus */*event*/,
5483 GObject */*tbl*/)
5484 {
5485 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5487 if (popdown_hasfocus) {
5488 gtk_widget_hide (popdown);
5489 popdown_hasfocus = false;
5490 popdown_visible = false;
5491 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5492 return TRUE;
5493 }
5494 return FALSE;
5495 }
5497 gboolean
5498 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5499 GdkEventFocus */*event*/,
5500 GObject */*tbl*/)
5501 {
5502 popdown_hasfocus = true;
5503 return TRUE;
5504 }
5507 void
5508 cell_data_func (GtkTreeViewColumn */*column*/,
5509 GtkCellRenderer *cell,
5510 GtkTreeModel *tree_model,
5511 GtkTreeIter *iter,
5512 gpointer /*data*/)
5513 {
5514 char *family,
5515 *family_escaped,
5516 *sample_escaped;
5518 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5520 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5522 family_escaped = g_markup_escape_text (family, -1);
5523 sample_escaped = g_markup_escape_text (sample, -1);
5525 std::stringstream markup;
5526 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5527 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5529 free (family);
5530 free (family_escaped);
5531 free (sample_escaped);
5532 }
5534 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5535 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5536 if (completion) {
5537 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5538 g_object_unref (completion);
5539 }
5540 }
5542 GtkWidget*
5543 sp_text_toolbox_new (SPDesktop *desktop)
5544 {
5545 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5546 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5548 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5549 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5551 GtkTooltips *tt = gtk_tooltips_new();
5552 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5554 ////////////Family
5555 //Window
5556 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5557 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5559 //Entry
5560 GtkWidget *entry = gtk_entry_new ();
5561 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5562 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5563 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5564 gtk_entry_completion_set_text_column (completion, 0);
5565 gtk_entry_completion_set_minimum_key_length (completion, 1);
5566 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5567 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5568 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5569 gtk_toolbar_append_widget( tbl, entry, "", "" );
5570 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5572 //Button
5573 GtkWidget *button = gtk_button_new ();
5574 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5575 gtk_toolbar_append_widget( tbl, button, "", "");
5577 //Popdown
5578 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5579 GtkWidget *treeview = gtk_tree_view_new ();
5581 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5582 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5583 gtk_tree_view_column_pack_start (column, cell, FALSE);
5584 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5585 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5586 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5588 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5589 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5590 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5592 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5594 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5595 gtk_container_add (GTK_CONTAINER (sw), treeview);
5597 gtk_container_add (GTK_CONTAINER (window), sw);
5598 gtk_widget_set_size_request (window, 300, 450);
5600 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5601 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5602 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5604 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5606 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5607 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5608 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5610 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5611 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5613 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5614 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5615 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5616 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5617 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5619 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5620 GtkWidget *box = gtk_event_box_new ();
5621 gtk_container_add (GTK_CONTAINER (box), image);
5622 gtk_toolbar_append_widget( tbl, box, "", "");
5623 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5624 GtkTooltips *tooltips = gtk_tooltips_new ();
5625 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5626 gtk_widget_hide (GTK_WIDGET (box));
5627 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5629 ////////////Size
5630 const char *sizes[] = {
5631 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5632 "16", "18", "20", "22", "24", "28",
5633 "32", "36", "40", "48", "56", "64", "72", "144"
5634 };
5636 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5637 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5638 gtk_widget_set_size_request (cbox, 80, -1);
5639 gtk_toolbar_append_widget( tbl, cbox, "", "");
5640 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5641 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5642 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5643 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5645 ////////////Text anchor
5646 GtkWidget *group = gtk_radio_button_new (NULL);
5647 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5648 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5650 // left
5651 GtkWidget *rbutton = group;
5652 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5653 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5654 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5656 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5657 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5658 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5659 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5661 // center
5662 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5663 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5664 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5665 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5667 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5668 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5669 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5670 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5672 // right
5673 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5674 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5675 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5676 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5678 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5679 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5680 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5681 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5683 // fill
5684 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5685 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5686 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5687 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5689 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5690 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5691 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5692 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5694 gtk_toolbar_append_widget( tbl, row, "", "");
5696 //spacer
5697 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5699 ////////////Text style
5700 row = gtk_hbox_new (FALSE, 4);
5702 // bold
5703 rbutton = gtk_toggle_button_new ();
5704 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5705 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5706 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5707 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5709 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5710 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5711 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5713 // italic
5714 rbutton = gtk_toggle_button_new ();
5715 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5716 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5717 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5718 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5720 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5721 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5722 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5724 gtk_toolbar_append_widget( tbl, row, "", "");
5726 //spacer
5727 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5729 ////////////Text orientation
5730 group = gtk_radio_button_new (NULL);
5731 row = gtk_hbox_new (FALSE, 4);
5732 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5734 // horizontal
5735 rbutton = group;
5736 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5737 gtk_container_add (GTK_CONTAINER (rbutton),
5738 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5739 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5740 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5742 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5743 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5744 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5746 // vertical
5747 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5748 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5749 gtk_container_add (GTK_CONTAINER (rbutton),
5750 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5751 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5752 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5754 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5755 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5756 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5757 gtk_toolbar_append_widget( tbl, row, "", "" );
5760 //watch selection
5761 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5763 sigc::connection *c_selection_changed =
5764 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5765 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5766 pool->add_connection ("selection-changed", c_selection_changed);
5768 sigc::connection *c_selection_modified =
5769 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5770 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5771 pool->add_connection ("selection-modified", c_selection_modified);
5773 sigc::connection *c_subselection_changed =
5774 new sigc::connection (desktop->connectToolSubselectionChanged
5775 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5776 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5778 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5781 gtk_widget_show_all( GTK_WIDGET(tbl) );
5783 return GTK_WIDGET(tbl);
5784 } // end of sp_text_toolbox_new()
5786 }//<unnamed> namespace
5789 //#########################
5790 //## Connector ##
5791 //#########################
5793 static void sp_connector_path_set_avoid(void)
5794 {
5795 cc_selection_set_avoid(true);
5796 }
5799 static void sp_connector_path_set_ignore(void)
5800 {
5801 cc_selection_set_avoid(false);
5802 }
5806 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5807 {
5808 // quit if run by the _changed callbacks
5809 if (g_object_get_data( tbl, "freeze" )) {
5810 return;
5811 }
5813 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5814 SPDocument *doc = sp_desktop_document(desktop);
5816 if (!sp_document_get_undo_sensitive(doc))
5817 {
5818 return;
5819 }
5821 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5823 if ( repr->attribute("inkscape:connector-spacing") ) {
5824 gdouble priorValue = gtk_adjustment_get_value(adj);
5825 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5826 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5827 return;
5828 }
5829 } else if ( adj->value == defaultConnSpacing ) {
5830 return;
5831 }
5833 // in turn, prevent callbacks from responding
5834 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5836 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5837 SP_OBJECT(desktop->namedview)->updateRepr();
5839 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5840 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5841 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5842 NR::Matrix m = NR::identity();
5843 avoid_item_move(&m, item);
5844 }
5846 if (items) {
5847 g_slist_free(items);
5848 }
5850 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5851 _("Change connector spacing"));
5853 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5855 spinbutton_defocus(GTK_OBJECT(tbl));
5856 }
5858 static void sp_connector_graph_layout(void)
5859 {
5860 if (!SP_ACTIVE_DESKTOP) return;
5862 // hack for clones, see comment in align-and-distribute.cpp
5863 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5864 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5866 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5868 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5870 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5871 }
5873 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5874 {
5875 if ( gtk_toggle_action_get_active( act ) ) {
5876 prefs_set_string_attribute("tools.connector", "directedlayout",
5877 "true");
5878 } else {
5879 prefs_set_string_attribute("tools.connector", "directedlayout",
5880 "false");
5881 }
5882 }
5884 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5885 {
5886 if ( gtk_toggle_action_get_active( act ) ) {
5887 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5888 "true");
5889 } else {
5890 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5891 "false");
5892 }
5893 }
5896 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5897 {
5898 prefs_set_double_attribute("tools.connector", "length", adj->value);
5899 spinbutton_defocus(GTK_OBJECT(tbl));
5900 }
5902 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5903 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5904 bool /*is_interactive*/, gpointer data)
5905 {
5906 GtkWidget *tbl = GTK_WIDGET(data);
5908 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5909 return;
5910 }
5911 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5912 return;
5913 }
5915 GtkAdjustment *adj = (GtkAdjustment*)
5916 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5917 gdouble spacing = defaultConnSpacing;
5918 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5920 gtk_adjustment_set_value(adj, spacing);
5921 }
5924 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5925 NULL, /* child_added */
5926 NULL, /* child_removed */
5927 connector_tb_event_attr_changed,
5928 NULL, /* content_changed */
5929 NULL /* order_changed */
5930 };
5933 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5934 {
5935 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5937 {
5938 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5939 _("Avoid"),
5940 _("Make connectors avoid selected objects"),
5941 "connector_avoid",
5942 secondarySize );
5943 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5944 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5945 }
5947 {
5948 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5949 _("Ignore"),
5950 _("Make connectors ignore selected objects"),
5951 "connector_ignore",
5952 secondarySize );
5953 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5954 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5955 }
5957 EgeAdjustmentAction* eact = 0;
5959 // Spacing spinbox
5960 eact = create_adjustment_action( "ConnectorSpacingAction",
5961 _("Connector Spacing"), _("Spacing:"),
5962 _("The amount of space left around objects by auto-routing connectors"),
5963 "tools.connector", "spacing", defaultConnSpacing,
5964 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5965 0, 100, 1.0, 10.0,
5966 0, 0, 0,
5967 connector_spacing_changed, 1, 0 );
5968 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5970 // Graph (connector network) layout
5971 {
5972 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5973 _("Graph"),
5974 _("Nicely arrange selected connector network"),
5975 "graph_layout",
5976 secondarySize );
5977 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5978 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5979 }
5981 // Default connector length spinbox
5982 eact = create_adjustment_action( "ConnectorLengthAction",
5983 _("Connector Length"), _("Length:"),
5984 _("Ideal length for connectors when layout is applied"),
5985 "tools.connector", "length", 100,
5986 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5987 10, 1000, 10.0, 100.0,
5988 0, 0, 0,
5989 connector_length_changed, 1, 0 );
5990 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5993 // Directed edges toggle button
5994 {
5995 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5996 _("Downwards"),
5997 _("Make connectors with end-markers (arrows) point downwards"),
5998 "directed_graph",
5999 Inkscape::ICON_SIZE_DECORATION );
6000 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6002 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6003 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6004 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6006 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6007 }
6009 // Avoid overlaps toggle button
6010 {
6011 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6012 _("Remove overlaps"),
6013 _("Do not allow overlapping shapes"),
6014 "remove_overlaps",
6015 Inkscape::ICON_SIZE_DECORATION );
6016 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6018 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6019 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6020 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6022 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6023 }
6025 // Code to watch for changes to the connector-spacing attribute in
6026 // the XML.
6027 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6028 g_assert(repr != NULL);
6030 purge_repr_listener( holder, holder );
6032 if (repr) {
6033 g_object_set_data( holder, "repr", repr );
6034 Inkscape::GC::anchor(repr);
6035 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6036 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6037 }
6038 } // end of sp_connector_toolbox_prep()
6041 //#########################
6042 //## Paintbucket ##
6043 //#########################
6045 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6046 {
6047 gint channels = ege_select_one_action_get_active( act );
6048 flood_channels_set_channels( channels );
6049 }
6051 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6052 {
6053 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6054 }
6056 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6057 {
6058 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6059 }
6061 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6062 {
6063 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6064 SPUnit const *unit = tracker->getActiveUnit();
6066 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6068 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6069 }
6071 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6072 {
6073 // FIXME: make defaults settable via Inkscape Options
6074 struct KeyValue {
6075 char const *key;
6076 double value;
6077 } const key_values[] = {
6078 {"threshold", 15},
6079 {"offset", 0.0}
6080 };
6082 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6083 KeyValue const &kv = key_values[i];
6084 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6085 if ( adj ) {
6086 gtk_adjustment_set_value(adj, kv.value);
6087 }
6088 }
6090 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6091 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6092 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6093 ege_select_one_action_set_active( autogap_action, 0 );
6094 }
6096 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6097 {
6098 EgeAdjustmentAction* eact = 0;
6100 {
6101 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6103 GList* items = 0;
6104 gint count = 0;
6105 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6106 {
6107 GtkTreeIter iter;
6108 gtk_list_store_append( model, &iter );
6109 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6110 count++;
6111 }
6112 g_list_free( items );
6113 items = 0;
6114 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6115 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6116 ege_select_one_action_set_appearance( act1, "compact" );
6117 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6118 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6119 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6120 g_object_set_data( holder, "channels_action", act1 );
6121 }
6123 // Spacing spinbox
6124 {
6125 eact = create_adjustment_action(
6126 "ThresholdAction",
6127 _("Fill Threshold"), _("Threshold:"),
6128 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6129 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6130 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6131 0, 0, 0,
6132 paintbucket_threshold_changed, 1, 0 );
6134 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6135 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6136 }
6138 // Create the units menu.
6139 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6140 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6141 if (stored_unit)
6142 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6143 g_object_set_data( holder, "tracker", tracker );
6144 {
6145 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6146 gtk_action_group_add_action( mainActions, act );
6147 }
6149 // Offset spinbox
6150 {
6151 eact = create_adjustment_action(
6152 "OffsetAction",
6153 _("Grow/shrink by"), _("Grow/shrink by:"),
6154 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6155 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6156 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6157 0, 0, 0,
6158 paintbucket_offset_changed, 1, 2);
6159 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6161 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6162 }
6164 /* Auto Gap */
6165 {
6166 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6168 GList* items = 0;
6169 gint count = 0;
6170 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6171 {
6172 GtkTreeIter iter;
6173 gtk_list_store_append( model, &iter );
6174 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6175 count++;
6176 }
6177 g_list_free( items );
6178 items = 0;
6179 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6180 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6181 ege_select_one_action_set_appearance( act2, "compact" );
6182 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6183 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6184 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6185 g_object_set_data( holder, "autogap_action", act2 );
6186 }
6188 /* Reset */
6189 {
6190 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6191 _("Defaults"),
6192 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6193 GTK_STOCK_CLEAR );
6194 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6195 gtk_action_group_add_action( mainActions, act );
6196 gtk_action_set_sensitive( act, TRUE );
6197 }
6199 }
6201 /*
6202 Local Variables:
6203 mode:c++
6204 c-file-style:"stroustrup"
6205 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6206 indent-tabs-mode:nil
6207 fill-column:99
6208 End:
6209 */
6210 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :