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