Code

marked forgotten translatable message
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
57 #include "live_effects/effect.h"
59 #include "inkscape.h"
60 #include "conn-avoid-ref.h"
63 #include "select-toolbar.h"
64 #include "gradient-toolbar.h"
66 #include "connector-context.h"
67 #include "node-context.h"
68 #include "draw-context.h"
69 #include "shape-editor.h"
70 #include "tweak-context.h"
71 #include "sp-rect.h"
72 #include "box3d.h"
73 #include "box3d-context.h"
74 #include "sp-star.h"
75 #include "sp-spiral.h"
76 #include "sp-ellipse.h"
77 #include "sp-text.h"
78 #include "sp-flowtext.h"
79 #include "sp-clippath.h"
80 #include "sp-mask.h"
81 #include "style.h"
82 #include "selection.h"
83 #include "selection-chemistry.h"
84 #include "document-private.h"
85 #include "desktop-style.h"
86 #include "../libnrtype/font-lister.h"
87 #include "../libnrtype/font-instance.h"
88 #include "../connection-pool.h"
89 #include "../prefs-utils.h"
90 #include "../inkscape-stock.h"
91 #include "icon.h"
92 #include "graphlayout/graphlayout.h"
94 #include "mod360.h"
96 #include "toolbox.h"
98 #include "flood-context.h"
100 #include "ink-action.h"
101 #include "ege-adjustment-action.h"
102 #include "ege-output-action.h"
103 #include "ege-select-one-action.h"
104 #include "helper/unit-tracker.h"
106 #include "svg/css-ostringstream.h"
108 #include "widgets/calligraphic-profile-rename.h"
110 using Inkscape::UnitTracker;
112 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
113 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
115 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
128 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
135 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
136     static Inkscape::IconSize sizeChoices[] = {
137         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
138         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
139         Inkscape::ICON_SIZE_MENU
140     };
141     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
142     return sizeChoices[index];
145 static struct {
146     gchar const *type_name;
147     gchar const *data_name;
148     sp_verb_t verb;
149     sp_verb_t doubleclick_verb;
150 } const tools[] = {
151     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
152     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
153     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
154     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
155     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
156     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
157     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
158     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
159     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
160     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
161     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
162     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
163     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
164     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
165     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
166     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
167     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
168     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
169     { NULL, NULL, 0, 0 }
170 };
172 static struct {
173     gchar const *type_name;
174     gchar const *data_name;
175     GtkWidget *(*create_func)(SPDesktop *desktop);
176     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
177     gchar const *ui_name;
178     gint swatch_verb_id;
179     gchar const *swatch_tool;
180     gchar const *swatch_tip;
181 } const aux_toolboxes[] = {
182     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
183       SP_VERB_INVALID, 0, 0},
184     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
187       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
188     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
189       SP_VERB_INVALID, 0, 0},
190     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
191       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
192     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
193       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
194     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
195       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
196     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
197       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
198     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
199       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
200     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
201       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
202     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
203       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
204     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
205       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
206     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
207       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
208     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
209       SP_VERB_INVALID, 0, 0},
210     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
211       SP_VERB_INVALID, 0, 0},
212     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
213       SP_VERB_INVALID, 0, 0},
214     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
215       SP_VERB_INVALID, 0, 0},
216     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
217       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
218     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
219 };
222 static gchar const * ui_descr =
223         "<ui>"
224         "  <toolbar name='SelectToolbar'>"
225         "    <toolitem action='EditSelectAll' />"
226         "    <toolitem action='EditSelectAllInAllLayers' />"
227         "    <toolitem action='EditDeselect' />"
228         "    <separator />"
229         "    <toolitem action='ObjectRotate90CCW' />"
230         "    <toolitem action='ObjectRotate90' />"
231         "    <toolitem action='ObjectFlipHorizontally' />"
232         "    <toolitem action='ObjectFlipVertically' />"
233         "    <separator />"
234         "    <toolitem action='SelectionToBack' />"
235         "    <toolitem action='SelectionLower' />"
236         "    <toolitem action='SelectionRaise' />"
237         "    <toolitem action='SelectionToFront' />"
238         "    <separator />"
239         "    <toolitem action='XAction' />"
240         "    <toolitem action='YAction' />"
241         "    <toolitem action='WidthAction' />"
242         "    <toolitem action='LockAction' />"
243         "    <toolitem action='HeightAction' />"
244         "    <toolitem action='UnitsAction' />"
245         "    <separator />"
246         "    <toolitem action='transform_affect_label' />"
247         "    <toolitem action='transform_stroke' />"
248         "    <toolitem action='transform_corners' />"
249         "    <toolitem action='transform_gradient' />"
250         "    <toolitem action='transform_pattern' />"
251         "  </toolbar>"
253         "  <toolbar name='NodeToolbar'>"
254         "    <toolitem action='NodeInsertAction' />"
255         "    <toolitem action='NodeDeleteAction' />"
256         "    <separator />"
257         "    <toolitem action='NodeJoinAction' />"
258         "    <toolitem action='NodeBreakAction' />"
259         "    <separator />"
260         "    <toolitem action='NodeJoinSegmentAction' />"
261         "    <toolitem action='NodeDeleteSegmentAction' />"
262         "    <separator />"
263         "    <toolitem action='NodeCuspAction' />"
264         "    <toolitem action='NodeSmoothAction' />"
265         "    <toolitem action='NodeSymmetricAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeLineAction' />"
268         "    <toolitem action='NodeCurveAction' />"
269         "    <separator />"
270         "    <toolitem action='ObjectToPath' />"
271         "    <toolitem action='StrokeToPath' />"
272         "    <separator />"
273         "    <toolitem action='NodeXAction' />"
274         "    <toolitem action='NodeYAction' />"
275         "    <toolitem action='NodeUnitsAction' />"
276         "    <separator />"
277         "    <toolitem action='ObjectEditClipPathAction' />"
278         "    <toolitem action='ObjectEditMaskPathAction' />"
279         "    <toolitem action='EditNextLPEParameterAction' />"
280         "    <separator />"
281         "    <toolitem action='NodesShowHandlesAction' />"
282         "    <toolitem action='NodesShowHelperpath' />"
283         "  </toolbar>"
285         "  <toolbar name='TweakToolbar'>"
286         "    <toolitem action='TweakWidthAction' />"
287         "    <separator />"
288         "    <toolitem action='TweakForceAction' />"
289         "    <toolitem action='TweakPressureAction' />"
290         "    <separator />"
291         "    <toolitem action='TweakModeAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakFidelityAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakChannelsLabel' />"
296         "    <toolitem action='TweakDoH' />"
297         "    <toolitem action='TweakDoS' />"
298         "    <toolitem action='TweakDoL' />"
299         "    <toolitem action='TweakDoO' />"
300         "  </toolbar>"
302         "  <toolbar name='ZoomToolbar'>"
303         "    <toolitem action='ZoomIn' />"
304         "    <toolitem action='ZoomOut' />"
305         "    <separator />"
306         "    <toolitem action='Zoom1:0' />"
307         "    <toolitem action='Zoom1:2' />"
308         "    <toolitem action='Zoom2:1' />"
309         "    <separator />"
310         "    <toolitem action='ZoomSelection' />"
311         "    <toolitem action='ZoomDrawing' />"
312         "    <toolitem action='ZoomPage' />"
313         "    <toolitem action='ZoomPageWidth' />"
314         "    <separator />"
315         "    <toolitem action='ZoomPrev' />"
316         "    <toolitem action='ZoomNext' />"
317         "  </toolbar>"
319         "  <toolbar name='StarToolbar'>"
320         "    <separator />"
321         "    <toolitem action='StarStateAction' />"
322         "    <separator />"
323         "    <toolitem action='FlatAction' />"
324         "    <separator />"
325         "    <toolitem action='MagnitudeAction' />"
326         "    <toolitem action='SpokeAction' />"
327         "    <toolitem action='RoundednessAction' />"
328         "    <toolitem action='RandomizationAction' />"
329         "    <separator />"
330         "    <toolitem action='StarResetAction' />"
331         "  </toolbar>"
333         "  <toolbar name='RectToolbar'>"
334         "    <toolitem action='RectStateAction' />"
335         "    <toolitem action='RectWidthAction' />"
336         "    <toolitem action='RectHeightAction' />"
337         "    <toolitem action='RadiusXAction' />"
338         "    <toolitem action='RadiusYAction' />"
339         "    <toolitem action='RectUnitsAction' />"
340         "    <separator />"
341         "    <toolitem action='RectResetAction' />"
342         "  </toolbar>"
344         "  <toolbar name='3DBoxToolbar'>"
345         "    <toolitem action='3DBoxAngleXAction' />"
346         "    <toolitem action='3DBoxVPXStateAction' />"
347         "    <separator />"
348         "    <toolitem action='3DBoxAngleYAction' />"
349         "    <toolitem action='3DBoxVPYStateAction' />"
350         "    <separator />"
351         "    <toolitem action='3DBoxAngleZAction' />"
352         "    <toolitem action='3DBoxVPZStateAction' />"
353         "  </toolbar>"
355         "  <toolbar name='SpiralToolbar'>"
356         "    <toolitem action='SpiralStateAction' />"
357         "    <toolitem action='SpiralRevolutionAction' />"
358         "    <toolitem action='SpiralExpansionAction' />"
359         "    <toolitem action='SpiralT0Action' />"
360         "    <separator />"
361         "    <toolitem action='SpiralResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='PenToolbar'>"
365         "    <toolitem action='SpiroSplineModeActionPen' />"
366         "  </toolbar>"
368         "  <toolbar name='PencilToolbar'>"
369         "    <toolitem action='SpiroSplineModeActionPencil' />"
370         "  </toolbar>"
372         "  <toolbar name='CalligraphyToolbar'>"
373         "    <separator />"
374         "    <toolitem action='SetProfileAction'/>"
375         "    <toolitem action='SaveDeleteProfileAction'/>"
376         "    <separator />"
377         "    <toolitem action='CalligraphyWidthAction' />"
378         "    <toolitem action='PressureAction' />"
379         "    <toolitem action='TraceAction' />"
380         "    <toolitem action='ThinningAction' />"
381         "    <separator />"
382         "    <toolitem action='AngleAction' />"
383         "    <toolitem action='TiltAction' />"
384         "    <toolitem action='FixationAction' />"
385         "    <separator />"
386         "    <toolitem action='CapRoundingAction' />"
387         "    <separator />"
388         "    <toolitem action='TremorAction' />"
389         "    <toolitem action='WiggleAction' />"
390         "    <toolitem action='MassAction' />"
391         "    <separator />"
392         "  </toolbar>"
394         "  <toolbar name='ArcToolbar'>"
395         "    <toolitem action='ArcStateAction' />"
396         "    <separator />"
397         "    <toolitem action='ArcStartAction' />"
398         "    <toolitem action='ArcEndAction' />"
399         "    <separator />"
400         "    <toolitem action='ArcOpenAction' />"
401         "    <separator />"
402         "    <toolitem action='ArcResetAction' />"
403         "    <separator />"
404         "  </toolbar>"
406         "  <toolbar name='PaintbucketToolbar'>"
407         "    <toolitem action='ChannelsAction' />"
408         "    <separator />"
409         "    <toolitem action='ThresholdAction' />"
410         "    <separator />"
411         "    <toolitem action='OffsetAction' />"
412         "    <toolitem action='PaintbucketUnitsAction' />"
413         "    <separator />"
414         "    <toolitem action='AutoGapAction' />"
415         "    <separator />"
416         "    <toolitem action='PaintbucketResetAction' />"
417         "  </toolbar>"
419         "  <toolbar name='EraserToolbar'>"
420         "    <toolitem action='EraserWidthAction' />"
421         "    <separator />"
422         "    <toolitem action='EraserModeAction' />"
423         "  </toolbar>"
425         "  <toolbar name='DropperToolbar'>"
426         "    <toolitem action='DropperOpacityAction' />"
427         "    <toolitem action='DropperPickAlphaAction' />"
428         "    <toolitem action='DropperSetAlphaAction' />"
429         "  </toolbar>"
431         "  <toolbar name='ConnectorToolbar'>"
432         "    <toolitem action='ConnectorAvoidAction' />"
433         "    <toolitem action='ConnectorIgnoreAction' />"
434         "    <toolitem action='ConnectorSpacingAction' />"
435         "    <toolitem action='ConnectorGraphAction' />"
436         "    <toolitem action='ConnectorLengthAction' />"
437         "    <toolitem action='ConnectorDirectedAction' />"
438         "    <toolitem action='ConnectorOverlapAction' />"
439         "  </toolbar>"
441         "</ui>"
444 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
446 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
448 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
449 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
451 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
452 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
454 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
455 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
457 /* Global text entry widgets necessary for update */
458 /* GtkWidget *dropper_rgb_entry,
459           *dropper_opacity_entry ; */
460 // should be made a private member once this is converted to class
462 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
463     connection->disconnect();
464     delete connection;
467 static void purge_repr_listener( GObject* obj, GObject* tbl )
469     (void)obj;
470     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
471     if (oldrepr) { // remove old listener
472         sp_repr_remove_listener_by_data(oldrepr, tbl);
473         Inkscape::GC::release(oldrepr);
474         oldrepr = 0;
475         g_object_set_data( tbl, "repr", NULL );
476     }
479 GtkWidget *
480 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
481                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
482                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
484     SPAction *action = verb->get_action(view);
485     if (!action) return NULL;
487     SPAction *doubleclick_action;
488     if (doubleclick_verb)
489         doubleclick_action = doubleclick_verb->get_action(view);
490     else
491         doubleclick_action = NULL;
493     /* fixme: Handle sensitive/unsensitive */
494     /* fixme: Implement sp_button_new_from_action */
495     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
496     gtk_widget_show(b);
497     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
499     return b;
502 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
503                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
505     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
508 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
509                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
511     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
515 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
517     SPAction* targetAction = SP_ACTION(user_data);
518     if ( targetAction ) {
519         sp_action_perform( targetAction, NULL );
520     }
523 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
525     if ( data ) {
526         GtkAction* act = GTK_ACTION(data);
527         gtk_action_set_sensitive( act, sensitive );
528     }
531 static SPActionEventVector action_event_vector = {
532     {NULL},
533     NULL,
534     NULL,
535     sp_action_action_set_sensitive,
536     NULL,
537     NULL
538 };
540 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
542     GtkAction* act = 0;
544     SPAction* targetAction = verb->get_action(view);
545     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
546     act = GTK_ACTION(inky);
547     gtk_action_set_sensitive( act, targetAction->sensitive );
549     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
551     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
552     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
554     return act;
557 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
559     Inkscape::UI::View::View *view = desktop;
560     gint verbsToUse[] = {
561         // disabled until we have icons for them:
562         //find
563         //SP_VERB_EDIT_TILE,
564         //SP_VERB_EDIT_UNTILE,
565         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
566         SP_VERB_DIALOG_DISPLAY,
567         SP_VERB_DIALOG_FILL_STROKE,
568         SP_VERB_DIALOG_NAMEDVIEW,
569         SP_VERB_DIALOG_TEXT,
570         SP_VERB_DIALOG_XML_EDITOR,
571         SP_VERB_EDIT_CLONE,
572         SP_VERB_EDIT_COPY,
573         SP_VERB_EDIT_CUT,
574         SP_VERB_EDIT_DUPLICATE,
575         SP_VERB_EDIT_PASTE,
576         SP_VERB_EDIT_REDO,
577         SP_VERB_EDIT_UNDO,
578         SP_VERB_EDIT_UNLINK_CLONE,
579         SP_VERB_FILE_EXPORT,
580         SP_VERB_FILE_IMPORT,
581         SP_VERB_FILE_NEW,
582         SP_VERB_FILE_OPEN,
583         SP_VERB_FILE_PRINT,
584         SP_VERB_FILE_SAVE,
585         SP_VERB_OBJECT_TO_CURVE,
586         SP_VERB_SELECTION_GROUP,
587         SP_VERB_SELECTION_OUTLINE,
588         SP_VERB_SELECTION_UNGROUP,
589         SP_VERB_ZOOM_1_1,
590         SP_VERB_ZOOM_1_2,
591         SP_VERB_ZOOM_2_1,
592         SP_VERB_ZOOM_DRAWING,
593         SP_VERB_ZOOM_IN,
594         SP_VERB_ZOOM_NEXT,
595         SP_VERB_ZOOM_OUT,
596         SP_VERB_ZOOM_PAGE,
597         SP_VERB_ZOOM_PAGE_WIDTH,
598         SP_VERB_ZOOM_PREV,
599         SP_VERB_ZOOM_SELECTION,
600     };
602     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
604     static std::map<SPDesktop*, GtkActionGroup*> groups;
605     GtkActionGroup* mainActions = 0;
606     if ( groups.find(desktop) != groups.end() ) {
607         mainActions = groups[desktop];
608     }
610     if ( !mainActions ) {
611         mainActions = gtk_action_group_new("main");
612         groups[desktop] = mainActions;
613     }
615     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
616         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
617         if ( verb ) {
618             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
619                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
620                 gtk_action_group_add_action( mainActions, act );
621             }
622         }
623     }
625     return mainActions;
629 GtkWidget *
630 sp_tool_toolbox_new()
632     GtkTooltips *tt = gtk_tooltips_new();
633     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
635     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
636     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
638     gtk_widget_set_sensitive(tb, FALSE);
640     GtkWidget *hb = gtk_handle_box_new();
641     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
642     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
643     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
645     gtk_container_add(GTK_CONTAINER(hb), tb);
646     gtk_widget_show(GTK_WIDGET(tb));
648     sigc::connection* conn = new sigc::connection;
649     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
651     return hb;
654 static void
655 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
657     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
658     gtk_widget_queue_resize(child);
661 static void
662 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
664     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
665     gtk_widget_queue_resize(child);
668 GtkWidget *
669 sp_aux_toolbox_new()
671     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
673     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
675     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
677     gtk_widget_set_sensitive(tb, FALSE);
679     GtkWidget *hb = gtk_handle_box_new();
680     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
681     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
682     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
684     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
685     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
687     gtk_container_add(GTK_CONTAINER(hb), tb);
688     gtk_widget_show(GTK_WIDGET(tb));
690     sigc::connection* conn = new sigc::connection;
691     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
693     return hb;
696 //####################################
697 //# Commands Bar
698 //####################################
700 GtkWidget *
701 sp_commands_toolbox_new()
703     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
705     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
707     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
708     gtk_widget_set_sensitive(tb, FALSE);
710     GtkWidget *hb = gtk_handle_box_new();
711     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
712     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
713     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
715     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
716     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
718     gtk_container_add(GTK_CONTAINER(hb), tb);
719     gtk_widget_show(GTK_WIDGET(tb));
721     sigc::connection* conn = new sigc::connection;
722     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
724     return hb;
727 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
728                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
729                                                        gchar const *path, gchar const *data, gdouble def,
730                                                        GtkWidget *focusTarget,
731                                                        GtkWidget *us,
732                                                        GObject *dataKludge,
733                                                        gboolean altx, gchar const *altx_mark,
734                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
735                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
736                                                        void (*callback)(GtkAdjustment *, GObject *),
737                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
739     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
740                                                              lower, upper, step, page, page ) );
741     if (us) {
742         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
743     }
745     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
747     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
748     if ( shortLabel ) {
749         g_object_set( act, "short_label", shortLabel, NULL );
750     }
752     if ( (descrCount > 0) && descrLabels && descrValues ) {
753         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
754     }
756     if ( focusTarget ) {
757         ege_adjustment_action_set_focuswidget( act, focusTarget );
758     }
760     if ( altx && altx_mark ) {
761         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
762     }
764     if ( dataKludge ) {
765         g_object_set_data( dataKludge, data, adj );
766     }
768     // Using a cast just to make sure we pass in the right kind of function pointer
769     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
771     return act;
775 //####################################
776 //# node editing callbacks
777 //####################################
779 /**
780  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
781  */
782 static ShapeEditor *get_current_shape_editor()
784     if (!SP_ACTIVE_DESKTOP) {
785         return NULL;
786     }
788     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
790     if (!SP_IS_NODE_CONTEXT(event_context)) {
791         return NULL;
792     }
794     return SP_NODE_CONTEXT(event_context)->shape_editor;
798 void
799 sp_node_path_edit_add(void)
801     ShapeEditor *shape_editor = get_current_shape_editor();
802     if (shape_editor) shape_editor->add_node();
805 void
806 sp_node_path_edit_delete(void)
808     ShapeEditor *shape_editor = get_current_shape_editor();
809     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
812 void
813 sp_node_path_edit_delete_segment(void)
815     ShapeEditor *shape_editor = get_current_shape_editor();
816     if (shape_editor) shape_editor->delete_segment();
819 void
820 sp_node_path_edit_break(void)
822     ShapeEditor *shape_editor = get_current_shape_editor();
823     if (shape_editor) shape_editor->break_at_nodes();
826 void
827 sp_node_path_edit_join(void)
829     ShapeEditor *shape_editor = get_current_shape_editor();
830     if (shape_editor) shape_editor->join_nodes();
833 void
834 sp_node_path_edit_join_segment(void)
836     ShapeEditor *shape_editor = get_current_shape_editor();
837     if (shape_editor) shape_editor->join_segments();
840 void
841 sp_node_path_edit_toline(void)
843     ShapeEditor *shape_editor = get_current_shape_editor();
844     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
847 void
848 sp_node_path_edit_tocurve(void)
850     ShapeEditor *shape_editor = get_current_shape_editor();
851     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
854 void
855 sp_node_path_edit_cusp(void)
857     ShapeEditor *shape_editor = get_current_shape_editor();
858     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
861 void
862 sp_node_path_edit_smooth(void)
864     ShapeEditor *shape_editor = get_current_shape_editor();
865     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
868 void
869 sp_node_path_edit_symmetrical(void)
871     ShapeEditor *shape_editor = get_current_shape_editor();
872     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
875 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
876     bool show = gtk_toggle_action_get_active( act );
877     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
878     ShapeEditor *shape_editor = get_current_shape_editor();
879     if (shape_editor) shape_editor->show_handles(show);
882 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
883     bool show = gtk_toggle_action_get_active( act );
884     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
885     ShapeEditor *shape_editor = get_current_shape_editor();
886     if (shape_editor) shape_editor->show_helperpath(show);
889 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
890     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
893 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
894     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
897 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
898     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
901 /* is called when the node selection is modified */
902 static void
903 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
905     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
906     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
907     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
908     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
910     // quit if run by the attr_changed listener
911     if (g_object_get_data( tbl, "freeze" )) {
912         return;
913     }
915     // in turn, prevent listener from responding
916     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
918     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
919     SPUnit const *unit = tracker->getActiveUnit();
921     ShapeEditor *shape_editor = get_current_shape_editor();
922     if (shape_editor && shape_editor->has_nodepath()) {
923         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
924         int n_selected = 0;
925         if (nodepath) {
926             n_selected = nodepath->numSelected();
927         }
929         if (n_selected == 0) {
930             gtk_action_set_sensitive(xact, FALSE);
931             gtk_action_set_sensitive(yact, FALSE);
932         } else {
933             gtk_action_set_sensitive(xact, TRUE);
934             gtk_action_set_sensitive(yact, TRUE);
935             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
936             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
938             if (n_selected == 1) {
939                 NR::Point sel_node = nodepath->singleSelectedCoords();
940                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
941                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
942                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
943                 }
944             } else {
945                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
946                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
947                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
948                     /* Note: Currently x and y will always have a value, even if the coordinates of the
949                        selected nodes don't coincide (in this case we use the coordinates of the center
950                        of the bounding box). So the entries are never set to zero. */
951                     // FIXME: Maybe we should clear the entry if several nodes are selected
952                     //        instead of providing a kind of average value
953                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
954                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
955                 }
956             }
957         }
958     } else {
959         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
960         gtk_action_set_sensitive(xact, FALSE);
961         gtk_action_set_sensitive(yact, FALSE);
962     }
964     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
967 static void
968 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
970     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
972     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
973     SPUnit const *unit = tracker->getActiveUnit();
975     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
976         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
977     }
979     // quit if run by the attr_changed listener
980     if (g_object_get_data( tbl, "freeze" )) {
981         return;
982     }
984     // in turn, prevent listener from responding
985     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
987     ShapeEditor *shape_editor = get_current_shape_editor();
988     if (shape_editor && shape_editor->has_nodepath()) {
989         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
990         if (!strcmp(value_name, "x")) {
991             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
992         }
993         if (!strcmp(value_name, "y")) {
994             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
995         }
996     }
998     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1001 static void
1002 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1004     sp_node_path_value_changed(adj, tbl, "x");
1007 static void
1008 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1010     sp_node_path_value_changed(adj, tbl, "y");
1013 void
1014 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1016     {
1017     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1018     SPItem *item = selection->singleItem();
1019     if (item && SP_IS_LPE_ITEM(item)) {
1020        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1021            gtk_action_set_sensitive(w, TRUE);
1022        } else {
1023            gtk_action_set_sensitive(w, FALSE);
1024        }
1025     } else {
1026        gtk_action_set_sensitive(w, FALSE);
1027     }
1028     }
1030     {
1031     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1032     SPItem *item = selection->singleItem();
1033     if (item && item->clip_ref && item->clip_ref->getObject()) {
1034        gtk_action_set_sensitive(w, TRUE);
1035     } else {
1036        gtk_action_set_sensitive(w, FALSE);
1037     }
1038     }
1040     {
1041     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1042     SPItem *item = selection->singleItem();
1043     if (item && item->mask_ref && item->mask_ref->getObject()) {
1044        gtk_action_set_sensitive(w, TRUE);
1045     } else {
1046        gtk_action_set_sensitive(w, FALSE);
1047     }
1048     }
1051 void
1052 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1054     sp_node_toolbox_sel_changed (selection, tbl);
1059 //################################
1060 //##    Node Editing Toolbox    ##
1061 //################################
1063 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1065     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1066     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1067     g_object_set_data( holder, "tracker", tracker );
1069     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1071     {
1072         InkAction* inky = ink_action_new( "NodeInsertAction",
1073                                           _("Insert node"),
1074                                           _("Insert new nodes into selected segments"),
1075                                           "node_insert",
1076                                           secondarySize );
1077         g_object_set( inky, "short_label", _("Insert"), NULL );
1078         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1079         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1080     }
1082     {
1083         InkAction* inky = ink_action_new( "NodeDeleteAction",
1084                                           _("Delete node"),
1085                                           _("Delete selected nodes"),
1086                                           "node_delete",
1087                                           secondarySize );
1088         g_object_set( inky, "short_label", _("Delete"), NULL );
1089         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1090         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1091     }
1093     {
1094         InkAction* inky = ink_action_new( "NodeJoinAction",
1095                                           _("Join endnodes"),
1096                                           _("Join selected endnodes"),
1097                                           "node_join",
1098                                           secondarySize );
1099         g_object_set( inky, "short_label", _("Join"), NULL );
1100         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1101         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1102     }
1104     {
1105         InkAction* inky = ink_action_new( "NodeBreakAction",
1106                                           _("Break nodes"),
1107                                           _("Break path at selected nodes"),
1108                                           "node_break",
1109                                           secondarySize );
1110         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1111         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1112     }
1115     {
1116         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1117                                           _("Join with segment"),
1118                                           _("Join selected endnodes with a new segment"),
1119                                           "node_join_segment",
1120                                           secondarySize );
1121         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1122         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1123     }
1125     {
1126         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1127                                           _("Delete segment"),
1128                                           _("Delete segment between two non-endpoint nodes"),
1129                                           "node_delete_segment",
1130                                           secondarySize );
1131         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1132         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1133     }
1135     {
1136         InkAction* inky = ink_action_new( "NodeCuspAction",
1137                                           _("Node Cusp"),
1138                                           _("Make selected nodes corner"),
1139                                           "node_cusp",
1140                                           secondarySize );
1141         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1142         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1143     }
1145     {
1146         InkAction* inky = ink_action_new( "NodeSmoothAction",
1147                                           _("Node Smooth"),
1148                                           _("Make selected nodes smooth"),
1149                                           "node_smooth",
1150                                           secondarySize );
1151         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1152         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1153     }
1155     {
1156         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1157                                           _("Node Symmetric"),
1158                                           _("Make selected nodes symmetric"),
1159                                           "node_symmetric",
1160                                           secondarySize );
1161         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1162         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1163     }
1165     {
1166         InkAction* inky = ink_action_new( "NodeLineAction",
1167                                           _("Node Line"),
1168                                           _("Make selected segments lines"),
1169                                           "node_line",
1170                                           secondarySize );
1171         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1172         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1173     }
1175     {
1176         InkAction* inky = ink_action_new( "NodeCurveAction",
1177                                           _("Node Curve"),
1178                                           _("Make selected segments curves"),
1179                                           "node_curve",
1180                                           secondarySize );
1181         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1182         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1183     }
1185     {
1186         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1187                                                       _("Show Handles"),
1188                                                       _("Show the Bezier handles of selected nodes"),
1189                                                       "nodes_show_handles",
1190                                                       Inkscape::ICON_SIZE_DECORATION );
1191         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1192         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1193         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1194     }
1196     {
1197         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1198                                                       _("Show Outline"),
1199                                                       _("Show the outline of the path"),
1200                                                       "nodes_show_helperpath",
1201                                                       Inkscape::ICON_SIZE_DECORATION );
1202         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1203         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1204         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1205     }
1207     {
1208         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1209                                           _("Next path effect parameter"),
1210                                           _("Show next path effect parameter for editing"),
1211                                           "edit_next_parameter",
1212                                           Inkscape::ICON_SIZE_DECORATION );
1213         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1214         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1215         g_object_set_data( holder, "nodes_lpeedit", inky);
1216     }
1218     {
1219         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1220                                           _("Edit clipping path"),
1221                                           _("Edit the clipping path of the object"),
1222                                           "nodeedit-clippath",
1223                                           Inkscape::ICON_SIZE_DECORATION );
1224         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1225         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1226         g_object_set_data( holder, "nodes_clippathedit", inky);
1227     }
1229     {
1230         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1231                                           _("Edit mask path"),
1232                                           _("Edit the mask of the object"),
1233                                           "nodeedit-mask",
1234                                           Inkscape::ICON_SIZE_DECORATION );
1235         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1236         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1237         g_object_set_data( holder, "nodes_maskedit", inky);
1238     }
1240     /* X coord of selected node(s) */
1241     {
1242         EgeAdjustmentAction* eact = 0;
1243         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1244         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1245         eact = create_adjustment_action( "NodeXAction",
1246                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1247                                          "tools.nodes", "Xcoord", 0,
1248                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1249                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1250                                          labels, values, G_N_ELEMENTS(labels),
1251                                          sp_node_path_x_value_changed );
1252         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1253         g_object_set_data( holder, "nodes_x_action", eact );
1254         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1255         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1256     }
1258     /* Y coord of selected node(s) */
1259     {
1260         EgeAdjustmentAction* eact = 0;
1261         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1262         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1263         eact = create_adjustment_action( "NodeYAction",
1264                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1265                                          "tools.nodes", "Ycoord", 0,
1266                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1267                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1268                                          labels, values, G_N_ELEMENTS(labels),
1269                                          sp_node_path_y_value_changed );
1270         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1271         g_object_set_data( holder, "nodes_y_action", eact );
1272         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1273         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1274     }
1276     // add the units menu
1277     {
1278         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1279         gtk_action_group_add_action( mainActions, act );
1280     }
1283     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1285     //watch selection
1286     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1288     sigc::connection *c_selection_changed =
1289         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1290                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1291     pool->add_connection ("selection-changed", c_selection_changed);
1293     sigc::connection *c_selection_modified =
1294         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1295                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1296     pool->add_connection ("selection-modified", c_selection_modified);
1298     sigc::connection *c_subselection_changed =
1299         new sigc::connection (desktop->connectToolSubselectionChanged
1300                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1301     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1303     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1305     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1306 } // end of sp_node_toolbox_prep()
1309 //########################
1310 //##    Zoom Toolbox    ##
1311 //########################
1313 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1315     // no custom GtkAction setup needed
1316 } // end of sp_zoom_toolbox_prep()
1318 void
1319 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1321     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_tool_toolbox, update_tool_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1325 void
1326 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1328     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_aux_toolbox, update_aux_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1331 void
1332 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1334     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_commands_toolbox, update_commands_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1337 static void
1338 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1340     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1341     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1343     if (old_desktop) {
1344         GList *children, *iter;
1346         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1347         for ( iter = children ; iter ; iter = iter->next ) {
1348             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1349         }
1350         g_list_free(children);
1351     }
1353     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1355     if (desktop) {
1356         gtk_widget_set_sensitive(toolbox, TRUE);
1357         setup_func(toolbox, desktop);
1358         update_func(desktop, desktop->event_context, toolbox);
1359         *conn = desktop->connectEventContextChanged
1360             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1361     } else {
1362         gtk_widget_set_sensitive(toolbox, FALSE);
1363     }
1365 } // end of toolbox_set_desktop()
1368 static void
1369 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1371     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1372     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1374     for (int i = 0 ; tools[i].type_name ; i++ ) {
1375         GtkWidget *button =
1376             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1377                                                               SP_BUTTON_TYPE_TOGGLE,
1378                                                               Inkscape::Verb::get(tools[i].verb),
1379                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1380                                                               desktop,
1381                                                               tooltips );
1383         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1384                            (gpointer)button );
1385     }
1389 static void
1390 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1392     gchar const *const tname = ( eventcontext
1393                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1394                                  : NULL );
1395     for (int i = 0 ; tools[i].type_name ; i++ ) {
1396         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1397         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1398     }
1401 static void
1402 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1404     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1405     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1406     GtkUIManager* mgr = gtk_ui_manager_new();
1407     GError* errVal = 0;
1408     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1409     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1411     std::map<std::string, GtkWidget*> dataHolders;
1413     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1414         if ( aux_toolboxes[i].prep_func ) {
1415             // converted to GtkActions and UIManager
1417             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1418             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1419             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1420             dataHolders[aux_toolboxes[i].type_name] = kludge;
1421             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1422         } else {
1424             GtkWidget *sub_toolbox = 0;
1425             if (aux_toolboxes[i].create_func == NULL)
1426                 sub_toolbox = sp_empty_toolbox_new(desktop);
1427             else {
1428                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1429             }
1431             gtk_size_group_add_widget( grouper, sub_toolbox );
1433             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1434             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1436         }
1437     }
1439     // Second pass to create toolbars *after* all GtkActions are created
1440     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1441         if ( aux_toolboxes[i].prep_func ) {
1442             // converted to GtkActions and UIManager
1444             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1446             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1447             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1449             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1450             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1451             g_free( tmp );
1452             tmp = 0;
1454             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1455             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1456                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1457             }
1458             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1461             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1463             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1464                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1465                 swatch->setDesktop( desktop );
1466                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1467                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1468                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1469                 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 );
1470             }
1472             gtk_widget_show_all( holder );
1473             sp_set_font_size_smaller( holder );
1475             gtk_size_group_add_widget( grouper, holder );
1477             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1478             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1479         }
1480     }
1482     g_object_unref( G_OBJECT(grouper) );
1485 static void
1486 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1488     gchar const *tname = ( eventcontext
1489                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1490                            : NULL );
1491     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1492         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1493         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1494             gtk_widget_show_all(sub_toolbox);
1495             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1496         } else {
1497             gtk_widget_hide(sub_toolbox);
1498         }
1499     }
1502 static void
1503 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1505     gchar const * descr =
1506         "<ui>"
1507         "  <toolbar name='CommandsToolbar'>"
1508         "    <toolitem action='FileNew' />"
1509         "    <toolitem action='FileOpen' />"
1510         "    <toolitem action='FileSave' />"
1511         "    <toolitem action='FilePrint' />"
1512         "    <separator />"
1513         "    <toolitem action='FileImport' />"
1514         "    <toolitem action='FileExport' />"
1515         "    <separator />"
1516         "    <toolitem action='EditUndo' />"
1517         "    <toolitem action='EditRedo' />"
1518         "    <separator />"
1519         "    <toolitem action='EditCopy' />"
1520         "    <toolitem action='EditCut' />"
1521         "    <toolitem action='EditPaste' />"
1522         "    <separator />"
1523         "    <toolitem action='ZoomSelection' />"
1524         "    <toolitem action='ZoomDrawing' />"
1525         "    <toolitem action='ZoomPage' />"
1526         "    <separator />"
1527         "    <toolitem action='EditDuplicate' />"
1528         "    <toolitem action='EditClone' />"
1529         "    <toolitem action='EditUnlinkClone' />"
1530         "    <separator />"
1531         "    <toolitem action='SelectionGroup' />"
1532         "    <toolitem action='SelectionUnGroup' />"
1533         "    <separator />"
1534         "    <toolitem action='DialogFillStroke' />"
1535         "    <toolitem action='DialogText' />"
1536         "    <toolitem action='DialogXMLEditor' />"
1537         "    <toolitem action='DialogAlignDistribute' />"
1538         "    <separator />"
1539         "    <toolitem action='DialogPreferences' />"
1540         "    <toolitem action='DialogDocumentProperties' />"
1541         "  </toolbar>"
1542         "</ui>";
1543     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1546     GtkUIManager* mgr = gtk_ui_manager_new();
1547     GError* errVal = 0;
1549     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1550     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1552     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1553     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1554         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1555     }
1557     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1558     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1561     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1564 static void
1565 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1569 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1571     gtk_widget_show(toolbox_toplevel);
1572     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1574     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1575     if (!shown_toolbox) {
1576         return;
1577     }
1578     gtk_widget_show(toolbox);
1580     gtk_widget_show_all(shown_toolbox);
1583 void
1584 aux_toolbox_space(GtkWidget *tb, gint space)
1586     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1589 static GtkWidget *
1590 sp_empty_toolbox_new(SPDesktop *desktop)
1592     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1593     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1594     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1596     gtk_widget_show_all(tbl);
1597     sp_set_font_size_smaller (tbl);
1599     return tbl;
1602 // helper UI functions
1604 GtkWidget *
1605 sp_tb_spinbutton(
1606     gchar *label, gchar const *tooltip,
1607     gchar const *path, gchar const *data, gdouble def,
1608     GtkWidget *us,
1609     GtkWidget *tbl,
1610     gboolean altx, gchar const *altx_mark,
1611     gdouble lower, gdouble upper, gdouble step, gdouble page,
1612     void (*callback)(GtkAdjustment *, GtkWidget *),
1613     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1615     GtkTooltips *tt = gtk_tooltips_new();
1617     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1619     GtkWidget *l = gtk_label_new(label);
1620     gtk_widget_show(l);
1621     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1622     gtk_container_add(GTK_CONTAINER(hb), l);
1624     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1625                                       lower, upper, step, page, page);
1626     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1627     if (us)
1628         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1630     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1631     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1632     if (altx)
1633         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1634     gtk_widget_set_size_request(sb,
1635                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1636                                 AUX_SPINBUTTON_HEIGHT);
1637     gtk_widget_show(sb);
1638     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1639     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1640     gtk_container_add(GTK_CONTAINER(hb), sb);
1641     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1643     return hb;
1646 #define MODE_LABEL_WIDTH 70
1648 //########################
1649 //##       Star         ##
1650 //########################
1652 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1654     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1656     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1657         // do not remember prefs if this call is initiated by an undo change, because undoing object
1658         // creation sets bogus values to its attributes before it is deleted
1659         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1660     }
1662     // quit if run by the attr_changed listener
1663     if (g_object_get_data( dataKludge, "freeze" )) {
1664         return;
1665     }
1667     // in turn, prevent listener from responding
1668     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1670     bool modmade = false;
1672     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1673     GSList const *items = selection->itemList();
1674     for (; items != NULL; items = items->next) {
1675         if (SP_IS_STAR((SPItem *) items->data)) {
1676             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1677             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1678             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1679                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1680                                     + M_PI / (gint)adj->value));
1681             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1682             modmade = true;
1683         }
1684     }
1685     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1686                                    _("Star: Change number of corners"));
1688     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1691 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1693     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1695     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1696         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1697     }
1699     // quit if run by the attr_changed listener
1700     if (g_object_get_data( dataKludge, "freeze" )) {
1701         return;
1702     }
1704     // in turn, prevent listener from responding
1705     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1707     bool modmade = false;
1708     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1709     GSList const *items = selection->itemList();
1710     for (; items != NULL; items = items->next) {
1711         if (SP_IS_STAR((SPItem *) items->data)) {
1712             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1714             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1715             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1716             if (r2 < r1) {
1717                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1718             } else {
1719                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1720             }
1722             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1723             modmade = true;
1724         }
1725     }
1727     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1728                                    _("Star: Change spoke ratio"));
1730     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1733 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1735     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1736     bool flat = ege_select_one_action_get_active( act ) == 0;
1738     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1739         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1740                                     flat ? "true" : "false" );
1741     }
1743     // quit if run by the attr_changed listener
1744     if (g_object_get_data( dataKludge, "freeze" )) {
1745         return;
1746     }
1748     // in turn, prevent listener from responding
1749     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1751     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1752     GSList const *items = selection->itemList();
1753     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1754     bool modmade = false;
1756     if ( prop_action ) {
1757         gtk_action_set_sensitive( prop_action, !flat );
1758     }
1760     for (; items != NULL; items = items->next) {
1761         if (SP_IS_STAR((SPItem *) items->data)) {
1762             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1763             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1764             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1765             modmade = true;
1766         }
1767     }
1769     if (modmade) {
1770         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1771                          flat ? _("Make polygon") : _("Make star"));
1772     }
1774     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1777 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1779     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1781     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1782         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1783     }
1785     // quit if run by the attr_changed listener
1786     if (g_object_get_data( dataKludge, "freeze" )) {
1787         return;
1788     }
1790     // in turn, prevent listener from responding
1791     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1793     bool modmade = false;
1795     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1796     GSList const *items = selection->itemList();
1797     for (; items != NULL; items = items->next) {
1798         if (SP_IS_STAR((SPItem *) items->data)) {
1799             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1800             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1801             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1802             modmade = true;
1803         }
1804     }
1805     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1806                                    _("Star: Change rounding"));
1808     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1811 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1813     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1815     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1816         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1817     }
1819     // quit if run by the attr_changed listener
1820     if (g_object_get_data( dataKludge, "freeze" )) {
1821         return;
1822     }
1824     // in turn, prevent listener from responding
1825     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1827     bool modmade = false;
1829     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1830     GSList const *items = selection->itemList();
1831     for (; items != NULL; items = items->next) {
1832         if (SP_IS_STAR((SPItem *) items->data)) {
1833             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1834             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1835             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1836             modmade = true;
1837         }
1838     }
1839     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1840                                    _("Star: Change randomization"));
1842     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1846 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1847                                        gchar const */*old_value*/, gchar const */*new_value*/,
1848                                        bool /*is_interactive*/, gpointer data)
1850     GtkWidget *tbl = GTK_WIDGET(data);
1852     // quit if run by the _changed callbacks
1853     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1854         return;
1855     }
1857     // in turn, prevent callbacks from responding
1858     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1860     GtkAdjustment *adj = 0;
1862     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1863     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1865     if (!strcmp(name, "inkscape:randomized")) {
1866         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1867         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1868     } else if (!strcmp(name, "inkscape:rounded")) {
1869         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1870         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1871     } else if (!strcmp(name, "inkscape:flatsided")) {
1872         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1873         char const *flatsides = repr->attribute("inkscape:flatsided");
1874         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1875         if ( flatsides && !strcmp(flatsides,"false") ) {
1876             ege_select_one_action_set_active( flat_action, 1 );
1877             gtk_action_set_sensitive( prop_action, TRUE );
1878         } else {
1879             ege_select_one_action_set_active( flat_action, 0 );
1880             gtk_action_set_sensitive( prop_action, FALSE );
1881         }
1882     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1883         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1884         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1885         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1886         if (r2 < r1) {
1887             gtk_adjustment_set_value(adj, r2/r1);
1888         } else {
1889             gtk_adjustment_set_value(adj, r1/r2);
1890         }
1891     } else if (!strcmp(name, "sodipodi:sides")) {
1892         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1893         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1894     }
1896     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1900 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1902     NULL, /* child_added */
1903     NULL, /* child_removed */
1904     star_tb_event_attr_changed,
1905     NULL, /* content_changed */
1906     NULL  /* order_changed */
1907 };
1910 /**
1911  *  \param selection Should not be NULL.
1912  */
1913 static void
1914 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1916     int n_selected = 0;
1917     Inkscape::XML::Node *repr = NULL;
1919     purge_repr_listener( tbl, tbl );
1921     for (GSList const *items = selection->itemList();
1922          items != NULL;
1923          items = items->next)
1924     {
1925         if (SP_IS_STAR((SPItem *) items->data)) {
1926             n_selected++;
1927             repr = SP_OBJECT_REPR((SPItem *) items->data);
1928         }
1929     }
1931     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1933     if (n_selected == 0) {
1934         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1935     } else if (n_selected == 1) {
1936         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1938         if (repr) {
1939             g_object_set_data( tbl, "repr", repr );
1940             Inkscape::GC::anchor(repr);
1941             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1942             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1943         }
1944     } else {
1945         // FIXME: implement averaging of all parameters for multiple selected stars
1946         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1947         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1948     }
1952 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1954     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1955     // callbacks to lump all the changes for all selected objects in one undo step
1957     GtkAdjustment *adj = 0;
1959     // fixme: make settable in prefs!
1960     gint mag = 5;
1961     gdouble prop = 0.5;
1962     gboolean flat = FALSE;
1963     gdouble randomized = 0;
1964     gdouble rounded = 0;
1966     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1967     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1969     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1970     gtk_action_set_sensitive( sb2, !flat );
1972     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1973     gtk_adjustment_set_value(adj, mag);
1974     gtk_adjustment_value_changed(adj);
1976     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1977     gtk_adjustment_set_value(adj, prop);
1978     gtk_adjustment_value_changed(adj);
1980     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1981     gtk_adjustment_set_value(adj, rounded);
1982     gtk_adjustment_value_changed(adj);
1984     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1985     gtk_adjustment_set_value(adj, randomized);
1986     gtk_adjustment_value_changed(adj);
1990 void
1991 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1993     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1994     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1995     GtkWidget *l = gtk_label_new(NULL);
1996     gtk_label_set_markup(GTK_LABEL(l), title);
1997     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1998     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1999     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2003 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2005     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2007     {
2008         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2009         ege_output_action_set_use_markup( act, TRUE );
2010         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2011         g_object_set_data( holder, "mode_action", act );
2012     }
2014     {
2015         EgeAdjustmentAction* eact = 0;
2016         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2017         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2019         /* Flatsided checkbox */
2020         {
2021             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2023             GtkTreeIter iter;
2024             gtk_list_store_append( model, &iter );
2025             gtk_list_store_set( model, &iter,
2026                                 0, _("Polygon"),
2027                                 1, _("Regular polygon (with one handle) instead of a star"),
2028                                 2, "star_flat",
2029                                 -1 );
2031             gtk_list_store_append( model, &iter );
2032             gtk_list_store_set( model, &iter,
2033                                 0, _("Star"),
2034                                 1, _("Star instead of a regular polygon (with one handle)"),
2035                                 2, "star_angled",
2036                                 -1 );
2038             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2039             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2040             g_object_set_data( holder, "flat_action", act );
2042             ege_select_one_action_set_appearance( act, "full" );
2043             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2044             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2045             ege_select_one_action_set_icon_column( act, 2 );
2046             ege_select_one_action_set_icon_size( act, secondarySize );
2047             ege_select_one_action_set_tooltip_column( act, 1  );
2049             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2050             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2051         }
2053         /* Magnitude */
2054         {
2055         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2056         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2057         eact = create_adjustment_action( "MagnitudeAction",
2058                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2059                                          "tools.shapes.star", "magnitude", 3,
2060                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2061                                          3, 1024, 1, 5,
2062                                          labels, values, G_N_ELEMENTS(labels),
2063                                          sp_stb_magnitude_value_changed,
2064                                          1.0, 0 );
2065         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2066         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2067         }
2069         /* Spoke ratio */
2070         {
2071         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2072         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2073         eact = create_adjustment_action( "SpokeAction",
2074                                          _("Spoke ratio"), _("Spoke ratio:"),
2075                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2076                                          // Base radius is the same for the closest handle.
2077                                          _("Base radius to tip radius ratio"),
2078                                          "tools.shapes.star", "proportion", 0.5,
2079                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2080                                          0.01, 1.0, 0.01, 0.1,
2081                                          labels, values, G_N_ELEMENTS(labels),
2082                                          sp_stb_proportion_value_changed );
2083         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2084         g_object_set_data( holder, "prop_action", eact );
2085         }
2087         if ( !isFlatSided ) {
2088             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2089         } else {
2090             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2091         }
2093         /* Roundedness */
2094         {
2095         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2096         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2097         eact = create_adjustment_action( "RoundednessAction",
2098                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2099                                          "tools.shapes.star", "rounded", 0.0,
2100                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2101                                          -10.0, 10.0, 0.01, 0.1,
2102                                          labels, values, G_N_ELEMENTS(labels),
2103                                          sp_stb_rounded_value_changed );
2104         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2105         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2106         }
2108         /* Randomization */
2109         {
2110         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2111         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2112         eact = create_adjustment_action( "RandomizationAction",
2113                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2114                                          "tools.shapes.star", "randomized", 0.0,
2115                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2116                                          -10.0, 10.0, 0.001, 0.01,
2117                                          labels, values, G_N_ELEMENTS(labels),
2118                                          sp_stb_randomized_value_changed, 0.1, 3 );
2119         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2120         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2121         }
2122     }
2124     {
2125         /* Reset */
2126         {
2127             GtkAction* act = gtk_action_new( "StarResetAction",
2128                                              _("Defaults"),
2129                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2130                                              GTK_STOCK_CLEAR );
2131             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2132             gtk_action_group_add_action( mainActions, act );
2133             gtk_action_set_sensitive( act, TRUE );
2134         }
2135     }
2137     sigc::connection *connection = new sigc::connection(
2138         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2139         );
2140     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2141     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2145 //########################
2146 //##       Rect         ##
2147 //########################
2149 static void sp_rtb_sensitivize( GObject *tbl )
2151     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2152     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2153     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2155     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2156         gtk_action_set_sensitive( not_rounded, FALSE );
2157     } else {
2158         gtk_action_set_sensitive( not_rounded, TRUE );
2159     }
2163 static void
2164 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2165                           void (*setter)(SPRect *, gdouble))
2167     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2169     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2170     SPUnit const *unit = tracker->getActiveUnit();
2172     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2173         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2174     }
2176     // quit if run by the attr_changed listener
2177     if (g_object_get_data( tbl, "freeze" )) {
2178         return;
2179     }
2181     // in turn, prevent listener from responding
2182     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2184     bool modmade = false;
2185     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2186     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2187         if (SP_IS_RECT(items->data)) {
2188             if (adj->value != 0) {
2189                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2190             } else {
2191                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2192             }
2193             modmade = true;
2194         }
2195     }
2197     sp_rtb_sensitivize( tbl );
2199     if (modmade) {
2200         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2201                                    _("Change rectangle"));
2202     }
2204     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2207 static void
2208 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2210     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2213 static void
2214 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2216     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2219 static void
2220 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2222     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2225 static void
2226 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2228     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2233 static void
2234 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2236     GtkAdjustment *adj = 0;
2238     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2239     gtk_adjustment_set_value(adj, 0.0);
2240     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2241     gtk_adjustment_value_changed(adj);
2243     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2244     gtk_adjustment_set_value(adj, 0.0);
2245     gtk_adjustment_value_changed(adj);
2247     sp_rtb_sensitivize( obj );
2250 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2251                                        gchar const */*old_value*/, gchar const */*new_value*/,
2252                                        bool /*is_interactive*/, gpointer data)
2254     GObject *tbl = G_OBJECT(data);
2256     // quit if run by the _changed callbacks
2257     if (g_object_get_data( tbl, "freeze" )) {
2258         return;
2259     }
2261     // in turn, prevent callbacks from responding
2262     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2264     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2265     SPUnit const *unit = tracker->getActiveUnit();
2267     gpointer item = g_object_get_data( tbl, "item" );
2268     if (item && SP_IS_RECT(item)) {
2269         {
2270             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2271             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2272             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2273         }
2275         {
2276             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2277             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2278             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2279         }
2281         {
2282             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2283             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2284             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2285         }
2287         {
2288             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2289             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2290             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2291         }
2292     }
2294     sp_rtb_sensitivize( tbl );
2296     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2300 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2301     NULL, /* child_added */
2302     NULL, /* child_removed */
2303     rect_tb_event_attr_changed,
2304     NULL, /* content_changed */
2305     NULL  /* order_changed */
2306 };
2308 /**
2309  *  \param selection should not be NULL.
2310  */
2311 static void
2312 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2314     int n_selected = 0;
2315     Inkscape::XML::Node *repr = NULL;
2316     SPItem *item = NULL;
2318     if ( g_object_get_data( tbl, "repr" ) ) {
2319         g_object_set_data( tbl, "item", NULL );
2320     }
2321     purge_repr_listener( tbl, tbl );
2323     for (GSList const *items = selection->itemList();
2324          items != NULL;
2325          items = items->next) {
2326         if (SP_IS_RECT((SPItem *) items->data)) {
2327             n_selected++;
2328             item = (SPItem *) items->data;
2329             repr = SP_OBJECT_REPR(item);
2330         }
2331     }
2333     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2335     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2337     if (n_selected == 0) {
2338         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2340         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2341         gtk_action_set_sensitive(w, FALSE);
2342         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2343         gtk_action_set_sensitive(h, FALSE);
2345     } else if (n_selected == 1) {
2346         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2347         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2349         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2350         gtk_action_set_sensitive(w, TRUE);
2351         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2352         gtk_action_set_sensitive(h, TRUE);
2354         if (repr) {
2355             g_object_set_data( tbl, "repr", repr );
2356             g_object_set_data( tbl, "item", item );
2357             Inkscape::GC::anchor(repr);
2358             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2359             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2360         }
2361     } else {
2362         // FIXME: implement averaging of all parameters for multiple selected
2363         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2364         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2365         sp_rtb_sensitivize( tbl );
2366     }
2370 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2372     EgeAdjustmentAction* eact = 0;
2373     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2375     {
2376         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2377         ege_output_action_set_use_markup( act, TRUE );
2378         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2379         g_object_set_data( holder, "mode_action", act );
2380     }
2382     // rx/ry units menu: create
2383     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2384     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2385     // fixme: add % meaning per cent of the width/height
2386     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2387     g_object_set_data( holder, "tracker", tracker );
2389     /* W */
2390     {
2391         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2392         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2393         eact = create_adjustment_action( "RectWidthAction",
2394                                          _("Width"), _("W:"), _("Width of rectangle"),
2395                                          "tools.shapes.rect", "width", 0,
2396                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2397                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2398                                          labels, values, G_N_ELEMENTS(labels),
2399                                          sp_rtb_width_value_changed );
2400         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2401         g_object_set_data( holder, "width_action", eact );
2402         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2403         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2404     }
2406     /* H */
2407     {
2408         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2409         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2410         eact = create_adjustment_action( "RectHeightAction",
2411                                          _("Height"), _("H:"), _("Height of rectangle"),
2412                                          "tools.shapes.rect", "height", 0,
2413                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2414                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2415                                          labels, values, G_N_ELEMENTS(labels),
2416                                          sp_rtb_height_value_changed );
2417         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2418         g_object_set_data( holder, "height_action", eact );
2419         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2420         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2421     }
2423     /* rx */
2424     {
2425         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2426         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2427         eact = create_adjustment_action( "RadiusXAction",
2428                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2429                                          "tools.shapes.rect", "rx", 0,
2430                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2431                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2432                                          labels, values, G_N_ELEMENTS(labels),
2433                                          sp_rtb_rx_value_changed);
2434         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2435         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2436     }
2438     /* ry */
2439     {
2440         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2441         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2442         eact = create_adjustment_action( "RadiusYAction",
2443                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2444                                          "tools.shapes.rect", "ry", 0,
2445                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2446                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2447                                          labels, values, G_N_ELEMENTS(labels),
2448                                          sp_rtb_ry_value_changed);
2449         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2450         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2451     }
2453     // add the units menu
2454     {
2455         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2456         gtk_action_group_add_action( mainActions, act );
2457     }
2459     /* Reset */
2460     {
2461         InkAction* inky = ink_action_new( "RectResetAction",
2462                                           _("Not rounded"),
2463                                           _("Make corners sharp"),
2464                                           "squared_corner",
2465                                           secondarySize );
2466         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2467         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2468         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2469         g_object_set_data( holder, "not_rounded", inky );
2470     }
2472     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2473     sp_rtb_sensitivize( holder );
2475     sigc::connection *connection = new sigc::connection(
2476         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2477         );
2478     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2479     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2482 //########################
2483 //##       3D Box       ##
2484 //########################
2486 // normalize angle so that it lies in the interval [0,360]
2487 static double box3d_normalize_angle (double a) {
2488     double angle = a + ((int) (a/360.0))*360;
2489     if (angle < 0) {
2490         angle += 360.0;
2491     }
2492     return angle;
2495 static void
2496 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2497                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2498     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2499     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2500     //       are reset).
2501     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2503     if (is_infinite) {
2504         gtk_toggle_action_set_active(tact, TRUE);
2505         gtk_action_set_sensitive(act, TRUE);
2507         double angle = persp3d_get_infinite_angle(persp, axis);
2508         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2509             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2510         }
2511     } else {
2512         gtk_toggle_action_set_active(tact, FALSE);
2513         gtk_action_set_sensitive(act, FALSE);
2514     }
2517 static void
2518 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2519     if (!persp_repr) {
2520         g_print ("No perspective given to box3d_resync_toolbar().\n");
2521         return;
2522     }
2524     GtkWidget *tbl = GTK_WIDGET(data);
2525     GtkAdjustment *adj = 0;
2526     GtkAction *act = 0;
2527     GtkToggleAction *tact = 0;
2528     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2529     {
2530         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2531         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2532         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2534         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2535     }
2536     {
2537         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2538         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2539         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2541         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2542     }
2543     {
2544         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2545         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2546         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2548         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2549     }
2552 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2553                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2554                                                   bool /*is_interactive*/, gpointer data)
2556     GtkWidget *tbl = GTK_WIDGET(data);
2558     // quit if run by the attr_changed listener
2559     // note: it used to work without the differently called freeze_ attributes (here and in
2560     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2561     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2562         return;
2563     }
2565     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2566     // sp_document_maybe_done() when the document is undo insensitive)
2567     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2569     // TODO: Only update the appropriate part of the toolbar
2570 //    if (!strcmp(name, "inkscape:vp_z")) {
2571         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2572 //    }
2574     Persp3D *persp = persp3d_get_from_repr(repr);
2575     persp3d_update_box_reprs(persp);
2577     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2580 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2582     NULL, /* child_added */
2583     NULL, /* child_removed */
2584     box3d_persp_tb_event_attr_changed,
2585     NULL, /* content_changed */
2586     NULL  /* order_changed */
2587 };
2589 /**
2590  *  \param selection Should not be NULL.
2591  */
2592 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2593 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2594 static void
2595 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2597     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2598     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2599     // update the perspectives with infinite VPs and leave the other ones untouched).
2601     Inkscape::XML::Node *persp_repr = NULL;
2602     purge_repr_listener(tbl, tbl);
2604     SPItem *item = selection->singleItem();
2605     if (item && SP_IS_BOX3D(item)) {
2606         // FIXME: Also deal with multiple selected boxes
2607         SPBox3D *box = SP_BOX3D(item);
2608         Persp3D *persp = box3d_get_perspective(box);
2609         persp_repr = SP_OBJECT_REPR(persp);
2610         if (persp_repr) {
2611             g_object_set_data(tbl, "repr", persp_repr);
2612             Inkscape::GC::anchor(persp_repr);
2613             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2614             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2615         }
2617         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2618         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2620         box3d_resync_toolbar(persp_repr, tbl);
2621     }
2624 static void
2625 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2627     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2628     SPDocument *document = sp_desktop_document(desktop);
2630     // quit if run by the attr_changed listener
2631     // note: it used to work without the differently called freeze_ attributes (here and in
2632     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2633     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2634         return;
2635     }
2637     // in turn, prevent listener from responding
2638     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2640     //Persp3D *persp = document->current_persp3d;
2641     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2642     if (sel_persps.empty()) {
2643         // this can happen when the document is created; we silently ignore it
2644         return;
2645     }
2646     Persp3D *persp = sel_persps.front();
2648     persp->tmat.set_infinite_direction (axis, adj->value);
2649     SP_OBJECT(persp)->updateRepr();
2651     // TODO: use the correct axis here, too
2652     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2654     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2658 static void
2659 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2661     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2664 static void
2665 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2667     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2670 static void
2671 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2673     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2677 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2679     // TODO: Take all selected perspectives into account
2680     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2681     if (sel_persps.empty()) {
2682         // this can happen when the document is created; we silently ignore it
2683         return;
2684     }
2685     Persp3D *persp = sel_persps.front();
2687     bool set_infinite = gtk_toggle_action_get_active(act);
2688     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2691 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2693     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2696 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2698     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2701 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2703     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2706 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2708     EgeAdjustmentAction* eact = 0;
2709     SPDocument *document = sp_desktop_document (desktop);
2710     Persp3D *persp = document->current_persp3d;
2712     EgeAdjustmentAction* box3d_angle_x = 0;
2713     EgeAdjustmentAction* box3d_angle_y = 0;
2714     EgeAdjustmentAction* box3d_angle_z = 0;
2716     /* Angle X */
2717     {
2718         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2719         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2720         eact = create_adjustment_action( "3DBoxAngleXAction",
2721                                          _("Angle in X direction"), _("Angle X:"),
2722                                          // Translators: PL is short for 'perspective line'
2723                                          _("Angle of PLs in X direction"),
2724                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2725                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2726                                          -360.0, 360.0, 1.0, 10.0,
2727                                          labels, values, G_N_ELEMENTS(labels),
2728                                          box3d_angle_x_value_changed );
2729         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2730         g_object_set_data( holder, "box3d_angle_x_action", eact );
2731         box3d_angle_x = eact;
2732     }
2734     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2735         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2736     } else {
2737         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2738     }
2741     /* VP X state */
2742     {
2743         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2744                                                       // Translators: VP is short for 'vanishing point'
2745                                                       _("State of VP in X direction"),
2746                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2747                                                       "toggle_vp_x",
2748                                                       Inkscape::ICON_SIZE_DECORATION );
2749         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2750         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2751         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2752         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2753         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2754     }
2756     /* Angle Y */
2757     {
2758         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2759         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2760         eact = create_adjustment_action( "3DBoxAngleYAction",
2761                                          _("Angle in Y direction"), _("Angle Y:"),
2762                                          // Translators: PL is short for 'perspective line'
2763                                          _("Angle of PLs in Y direction"),
2764                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2765                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2766                                          -360.0, 360.0, 1.0, 10.0,
2767                                          labels, values, G_N_ELEMENTS(labels),
2768                                          box3d_angle_y_value_changed );
2769         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2770         g_object_set_data( holder, "box3d_angle_y_action", eact );
2771         box3d_angle_y = eact;
2772     }
2774     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2775         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2776     } else {
2777         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2778     }
2780     /* VP Y state */
2781     {
2782         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2783                                                       // Translators: VP is short for 'vanishing point'
2784                                                       _("State of VP in Y direction"),
2785                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2786                                                       "toggle_vp_y",
2787                                                       Inkscape::ICON_SIZE_DECORATION );
2788         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2789         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2790         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2791         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2792         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2793     }
2795     /* Angle Z */
2796     {
2797         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2798         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2799         eact = create_adjustment_action( "3DBoxAngleZAction",
2800                                          _("Angle in Z direction"), _("Angle Z:"),
2801                                          // Translators: PL is short for 'perspective line'
2802                                          _("Angle of PLs in Z direction"),
2803                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2804                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2805                                          -360.0, 360.0, 1.0, 10.0,
2806                                          labels, values, G_N_ELEMENTS(labels),
2807                                          box3d_angle_z_value_changed );
2808         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2809         g_object_set_data( holder, "box3d_angle_z_action", eact );
2810         box3d_angle_z = eact;
2811     }
2813     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2814         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2815     } else {
2816         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2817     }
2819     /* VP Z state */
2820     {
2821         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2822                                                       // Translators: VP is short for 'vanishing point'
2823                                                       _("State of VP in Z direction"),
2824                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2825                                                       "toggle_vp_z",
2826                                                       Inkscape::ICON_SIZE_DECORATION );
2827         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2828         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2829         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2830         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2831         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2832     }
2834     sigc::connection *connection = new sigc::connection(
2835         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2836        );
2837     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2838     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2841 //########################
2842 //##       Spiral       ##
2843 //########################
2845 static void
2846 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2848     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2850     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2851         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2852     }
2854     // quit if run by the attr_changed listener
2855     if (g_object_get_data( tbl, "freeze" )) {
2856         return;
2857     }
2859     // in turn, prevent listener from responding
2860     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2862     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2864     bool modmade = false;
2865     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2866          items != NULL;
2867          items = items->next)
2868     {
2869         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2870             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2871             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2872             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2873             modmade = true;
2874         }
2875     }
2877     g_free(namespaced_name);
2879     if (modmade) {
2880         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2881                                    _("Change spiral"));
2882     }
2884     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2887 static void
2888 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2890     sp_spl_tb_value_changed(adj, tbl, "revolution");
2893 static void
2894 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2896     sp_spl_tb_value_changed(adj, tbl, "expansion");
2899 static void
2900 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2902     sp_spl_tb_value_changed(adj, tbl, "t0");
2905 static void
2906 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2908     GtkWidget *tbl = GTK_WIDGET(obj);
2910     GtkAdjustment *adj;
2912     // fixme: make settable
2913     gdouble rev = 5;
2914     gdouble exp = 1.0;
2915     gdouble t0 = 0.0;
2917     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2918     gtk_adjustment_set_value(adj, rev);
2919     gtk_adjustment_value_changed(adj);
2921     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2922     gtk_adjustment_set_value(adj, exp);
2923     gtk_adjustment_value_changed(adj);
2925     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2926     gtk_adjustment_set_value(adj, t0);
2927     gtk_adjustment_value_changed(adj);
2929     spinbutton_defocus(GTK_OBJECT(tbl));
2933 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2934                                          gchar const */*old_value*/, gchar const */*new_value*/,
2935                                          bool /*is_interactive*/, gpointer data)
2937     GtkWidget *tbl = GTK_WIDGET(data);
2939     // quit if run by the _changed callbacks
2940     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2941         return;
2942     }
2944     // in turn, prevent callbacks from responding
2945     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2947     GtkAdjustment *adj;
2948     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2949     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2951     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2952     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2954     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2955     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2957     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2961 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2962     NULL, /* child_added */
2963     NULL, /* child_removed */
2964     spiral_tb_event_attr_changed,
2965     NULL, /* content_changed */
2966     NULL  /* order_changed */
2967 };
2969 static void
2970 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2972     int n_selected = 0;
2973     Inkscape::XML::Node *repr = NULL;
2975     purge_repr_listener( tbl, tbl );
2977     for (GSList const *items = selection->itemList();
2978          items != NULL;
2979          items = items->next)
2980     {
2981         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2982             n_selected++;
2983             repr = SP_OBJECT_REPR((SPItem *) items->data);
2984         }
2985     }
2987     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2989     if (n_selected == 0) {
2990         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2991     } else if (n_selected == 1) {
2992         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2994         if (repr) {
2995             g_object_set_data( tbl, "repr", repr );
2996             Inkscape::GC::anchor(repr);
2997             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2998             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2999         }
3000     } else {
3001         // FIXME: implement averaging of all parameters for multiple selected
3002         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3003         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3004     }
3008 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3010     EgeAdjustmentAction* eact = 0;
3011     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3013     {
3014         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3015         ege_output_action_set_use_markup( act, TRUE );
3016         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3017         g_object_set_data( holder, "mode_action", act );
3018     }
3020     /* Revolution */
3021     {
3022         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3023         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3024         eact = create_adjustment_action( "SpiralRevolutionAction",
3025                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3026                                          "tools.shapes.spiral", "revolution", 3.0,
3027                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3028                                          0.01, 1024.0, 0.1, 1.0,
3029                                          labels, values, G_N_ELEMENTS(labels),
3030                                          sp_spl_tb_revolution_value_changed, 1, 2);
3031         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3032     }
3034     /* Expansion */
3035     {
3036         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3037         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3038         eact = create_adjustment_action( "SpiralExpansionAction",
3039                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3040                                          "tools.shapes.spiral", "expansion", 1.0,
3041                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3042                                          0.0, 1000.0, 0.01, 1.0,
3043                                          labels, values, G_N_ELEMENTS(labels),
3044                                          sp_spl_tb_expansion_value_changed);
3045         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3046     }
3048     /* T0 */
3049     {
3050         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3051         gdouble values[] = {0, 0.5, 0.9};
3052         eact = create_adjustment_action( "SpiralT0Action",
3053                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3054                                          "tools.shapes.spiral", "t0", 0.0,
3055                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3056                                          0.0, 0.999, 0.01, 1.0,
3057                                          labels, values, G_N_ELEMENTS(labels),
3058                                          sp_spl_tb_t0_value_changed);
3059         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3060     }
3062     /* Reset */
3063     {
3064         InkAction* inky = ink_action_new( "SpiralResetAction",
3065                                           _("Defaults"),
3066                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3067                                           GTK_STOCK_CLEAR,
3068                                           secondarySize );
3069         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3070         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3071     }
3074     sigc::connection *connection = new sigc::connection(
3075         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3076         );
3077     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3078     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3081 //########################
3082 //##     Pen/Pencil    ##
3083 //########################
3085 static void sp_pc_spiro_spline_mode_changed(GtkToggleAction *act, GObject* /*tbl*/)
3087     prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", gtk_toggle_action_get_active(act) ? 1 : 0);
3090 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, const char* action_name)
3092     /* Spiro Spline Mode toggle button */
3093     {
3094         InkToggleAction* act = ink_toggle_action_new(action_name,
3095                                                      _("Spiro Spline Mode"),
3096                                                      _("Automatically apply the 'Spiro Spline' live path effect to newly drawn paths"),
3097                                                      "spiro_splines_mode",
3098                                                      Inkscape::ICON_SIZE_DECORATION );
3099         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
3100         g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3101         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0));
3102         g_object_set_data( holder, "spiro_spline_mode", act );
3103     }
3106 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3108     sp_add_spiro_toggle(mainActions, holder, "SpiroSplineModeActionPen");
3111 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3113     sp_add_spiro_toggle(mainActions, holder, "SpiroSplineModeActionPencil");
3116 //########################
3117 //##       Tweak        ##
3118 //########################
3120 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3122     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3125 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3127     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3130 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3132     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3135 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3137     int mode = ege_select_one_action_get_active( act );
3138     prefs_set_int_attribute("tools.tweak", "mode", mode);
3140     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3141     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3142     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3143     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3144     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3145     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3146     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3147         if (doh) gtk_action_set_sensitive (doh, TRUE);
3148         if (dos) gtk_action_set_sensitive (dos, TRUE);
3149         if (dol) gtk_action_set_sensitive (dol, TRUE);
3150         if (doo) gtk_action_set_sensitive (doo, TRUE);
3151         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3152         if (fid) gtk_action_set_sensitive (fid, FALSE);
3153     } else {
3154         if (doh) gtk_action_set_sensitive (doh, FALSE);
3155         if (dos) gtk_action_set_sensitive (dos, FALSE);
3156         if (dol) gtk_action_set_sensitive (dol, FALSE);
3157         if (doo) gtk_action_set_sensitive (doo, FALSE);
3158         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3159         if (fid) gtk_action_set_sensitive (fid, TRUE);
3160     }
3163 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3165     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3168 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3169     bool show = gtk_toggle_action_get_active( act );
3170     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3172 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3173     bool show = gtk_toggle_action_get_active( act );
3174     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3176 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3177     bool show = gtk_toggle_action_get_active( act );
3178     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3180 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3181     bool show = gtk_toggle_action_get_active( act );
3182     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3185 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3187     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3189     {
3190         /* Width */
3191         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3192         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3193         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3194                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3195                                                               "tools.tweak", "width", 15,
3196                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3197                                                               1, 100, 1.0, 10.0,
3198                                                               labels, values, G_N_ELEMENTS(labels),
3199                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3200         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3201         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3202     }
3205     {
3206         /* Force */
3207         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3208         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3209         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3210                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3211                                                               "tools.tweak", "force", 20,
3212                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3213                                                               1, 100, 1.0, 10.0,
3214                                                               labels, values, G_N_ELEMENTS(labels),
3215                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3216         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3217         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3218     }
3220     /* Mode */
3221     {
3222         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3224         GtkTreeIter iter;
3225         gtk_list_store_append( model, &iter );
3226         gtk_list_store_set( model, &iter,
3227                             0, _("Push mode"),
3228                             1, _("Push parts of paths in any direction"),
3229                             2, "tweak_push_mode",
3230                             -1 );
3232         gtk_list_store_append( model, &iter );
3233         gtk_list_store_set( model, &iter,
3234                             0, _("Shrink mode"),
3235                             1, _("Shrink (inset) parts of paths"),
3236                             2, "tweak_shrink_mode",
3237                             -1 );
3239         gtk_list_store_append( model, &iter );
3240         gtk_list_store_set( model, &iter,
3241                             0, _("Grow mode"),
3242                             1, _("Grow (outset) parts of paths"),
3243                             2, "tweak_grow_mode",
3244                             -1 );
3246         gtk_list_store_append( model, &iter );
3247         gtk_list_store_set( model, &iter,
3248                             0, _("Attract mode"),
3249                             1, _("Attract parts of paths towards cursor"),
3250                             2, "tweak_attract_mode",
3251                             -1 );
3253         gtk_list_store_append( model, &iter );
3254         gtk_list_store_set( model, &iter,
3255                             0, _("Repel mode"),
3256                             1, _("Repel parts of paths from cursor"),
3257                             2, "tweak_repel_mode",
3258                             -1 );
3260         gtk_list_store_append( model, &iter );
3261         gtk_list_store_set( model, &iter,
3262                             0, _("Roughen mode"),
3263                             1, _("Roughen parts of paths"),
3264                             2, "tweak_roughen_mode",
3265                             -1 );
3267         gtk_list_store_append( model, &iter );
3268         gtk_list_store_set( model, &iter,
3269                             0, _("Color paint mode"),
3270                             1, _("Paint the tool's color upon selected objects"),
3271                             2, "tweak_colorpaint_mode",
3272                             -1 );
3274         gtk_list_store_append( model, &iter );
3275         gtk_list_store_set( model, &iter,
3276                             0, _("Color jitter mode"),
3277                             1, _("Jitter the colors of selected objects"),
3278                             2, "tweak_colorjitter_mode",
3279                             -1 );
3281         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3282         g_object_set( act, "short_label", _("Mode:"), NULL );
3283         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3284         g_object_set_data( holder, "mode_action", act );
3286         ege_select_one_action_set_appearance( act, "full" );
3287         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3288         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3289         ege_select_one_action_set_icon_column( act, 2 );
3290         ege_select_one_action_set_icon_size( act, secondarySize );
3291         ege_select_one_action_set_tooltip_column( act, 1  );
3293         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3294         ege_select_one_action_set_active( act, mode );
3295         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3297         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3298     }
3300     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3302     {
3303         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3304         ege_output_action_set_use_markup( act, TRUE );
3305         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3306         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3307             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3308         g_object_set_data( holder, "tweak_channels_label", act);
3309     }
3311     {
3312         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3313                                                       _("Hue"),
3314                                                       _("In color mode, act on objects' hue"),
3315                                                       NULL,
3316                                                       Inkscape::ICON_SIZE_DECORATION );
3317         //TRANSLATORS:  "H" here stands for hue
3318         g_object_set( act, "short_label", _("H"), NULL );
3319         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3320         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3321         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3322         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3323             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3324         g_object_set_data( holder, "tweak_doh", act);
3325     }
3326     {
3327         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3328                                                       _("Saturation"),
3329                                                       _("In color mode, act on objects' saturation"),
3330                                                       NULL,
3331                                                       Inkscape::ICON_SIZE_DECORATION );
3332         //TRANSLATORS: "S" here stands for Saturation
3333         g_object_set( act, "short_label", _("S"), NULL );
3334         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3335         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3336         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3337         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3338             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3339         g_object_set_data( holder, "tweak_dos", act );
3340     }
3341     {
3342         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3343                                                       _("Lightness"),
3344                                                       _("In color mode, act on objects' lightness"),
3345                                                       NULL,
3346                                                       Inkscape::ICON_SIZE_DECORATION );
3347         //TRANSLATORS: "L" here stands for Lightness
3348         g_object_set( act, "short_label", _("L"), NULL );
3349         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3350         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3351         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3352         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3353             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3354         g_object_set_data( holder, "tweak_dol", act );
3355     }
3356     {
3357         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3358                                                       _("Opacity"),
3359                                                       _("In color mode, act on objects' opacity"),
3360                                                       NULL,
3361                                                       Inkscape::ICON_SIZE_DECORATION );
3362         //TRANSLATORS: "O" here stands for Opacity
3363         g_object_set( act, "short_label", _("O"), NULL );
3364         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3365         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3366         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3367         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3368             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3369         g_object_set_data( holder, "tweak_doo", act );
3370     }
3372     {   /* Fidelity */
3373         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3374         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3375         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3376                                                               _("Fidelity"), _("Fidelity:"),
3377                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3378                                                               "tools.tweak", "fidelity", 50,
3379                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3380                                                               1, 100, 1.0, 10.0,
3381                                                               labels, values, G_N_ELEMENTS(labels),
3382                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3383         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3384         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3385         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3386             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3387         g_object_set_data( holder, "tweak_fidelity", eact );
3388     }
3391     /* Use Pressure button */
3392     {
3393         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3394                                                       _("Pressure"),
3395                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3396                                                       "use_pressure",
3397                                                       Inkscape::ICON_SIZE_DECORATION );
3398         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3399         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3400         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3401     }
3406 //########################
3407 //##     Calligraphy    ##
3408 //########################
3409 static void update_presets_list(GObject *dataKludge ){
3410     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3411     if (sel) {
3412         ege_select_one_action_set_active(sel, 0 );
3413     }
3416 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3418     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3419     update_presets_list(tbl);
3422 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3424     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3425     update_presets_list(tbl);
3428 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3430     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3431     update_presets_list(tbl);
3434 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3436     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3437     update_presets_list(tbl);
3440 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3442     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3443     update_presets_list(tbl);
3446 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3448     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3449     update_presets_list(tbl);
3452 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3454     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3455     update_presets_list(tbl);
3458 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3460     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3461     update_presets_list(tbl);
3464 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3466     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3467     update_presets_list(tbl);
3470 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3472     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3473     update_presets_list(tbl);
3476 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3478     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3479     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3480     update_presets_list(tbl);
3481     if (calligraphy_angle )
3482         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3486 #define PROFILE_FLOAT_SIZE 7
3487 #define PROFILE_INT_SIZE 4
3488 struct ProfileFloatElement {
3489     char const *name;
3490     double def;
3491     double min;
3492     double max;
3493 };
3494 struct ProfileIntElement {
3495     char const *name;
3496     int def;
3497     int min;
3498     int max;
3499 };
3503 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3504     {"mass",0.02, 0.0, 1.0},
3505     {"wiggle",0.0, 0.0, 1.0},
3506     {"angle",30.0, -90.0, 90.0},
3507     {"thinning",0.1, -1.0, 1.0},
3508     {"tremor",0.0, 0.0, 1.0},
3509     {"flatness",0.9, 0.0, 1.0},
3510     {"cap_rounding",0.0, 0.0, 5.0}
3511 };
3512 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3513     {"width",15, 1, 100},
3514     {"usepressure",1,0,1},
3515     {"tracebackground",0,0,1},
3516     {"usetilt",1,0,1},
3517 };
3521 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3522     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3523     if (! desktop) return;
3525     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3526     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3527     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3529     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3530     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3531     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3532     
3533     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3534         ProfileFloatElement const &pe = f_profile[i];
3535         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3536         prefs_set_double_attribute(pref_path,pe.name,v);
3537     }    
3538     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3539         ProfileIntElement const &pe = i_profile[i];
3540         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3541         prefs_set_int_attribute(pref_path,pe.name,v);
3542     }
3543     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3545     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3546     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3547     GtkTreeIter iter;
3548     gtk_list_store_append( model, &iter );
3549     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3551     free(profile_id);
3552     free(pref_path);
3554     ege_select_one_action_set_active(selector, new_index);
3558 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3559     
3560     gint preset_index = ege_select_one_action_get_active( act ); 
3561     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3562        
3563     if ( profile_name) {        
3564         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3565         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3566             ProfileFloatElement const &pe = f_profile[i];
3567             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3568             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3569             if ( adj ) {
3570                 gtk_adjustment_set_value(adj, v);
3571             }
3572         } 
3573         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3574             ProfileIntElement const &pe = i_profile[i];
3575             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3576             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3577             if ( toggle ) {
3578                 gtk_toggle_action_set_active(toggle, v);
3579             } else printf("No toggle");
3580         }
3581         free(profile_name);
3582         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3583     }
3588 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3590     {
3591         EgeAdjustmentAction* calligraphy_angle = 0;
3593         {
3594         /* Width */
3595         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3596         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3597         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3598                                                               _("Pen Width"), _("Width:"),
3599                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3600                                                               "tools.calligraphic", "width", 15,
3601                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3602                                                               1, 100, 1.0, 10.0,
3603                                                               labels, values, G_N_ELEMENTS(labels),
3604                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3605         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3606         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3607         }
3609         {
3610         /* Thinning */
3611             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3612             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3613         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3614                                                               _("Stroke Thinning"), _("Thinning:"),
3615                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3616                                                               "tools.calligraphic", "thinning", 0.1,
3617                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3618                                                               -1.0, 1.0, 0.01, 0.1,
3619                                                               labels, values, G_N_ELEMENTS(labels),
3620                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3621         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3622         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3623         }
3625         {
3626         /* Angle */
3627         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3628         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3629         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3630                                                               _("Pen Angle"), _("Angle:"),
3631                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3632                                                               "tools.calligraphic", "angle", 30,
3633                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3634                                                               -90.0, 90.0, 1.0, 10.0,
3635                                                               labels, values, G_N_ELEMENTS(labels),
3636                                                               sp_ddc_angle_value_changed, 1, 0 );
3637         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3638         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3639         g_object_set_data( holder, "angle", eact );
3640         calligraphy_angle = eact;
3641         }
3643         {
3644         /* Fixation */
3645             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3646         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3647         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3648                                                               _("Fixation"), _("Fixation:"),
3649                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3650                                                               "tools.calligraphic", "flatness", 0.9,
3651                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3652                                                               0.0, 1.0, 0.01, 0.1,
3653                                                               labels, values, G_N_ELEMENTS(labels),
3654                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3655         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3656         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3657         }
3659         {
3660         /* Cap Rounding */
3661             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3662         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3663         // TRANSLATORS: "cap" means "end" (both start and finish) here
3664         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3665                                                               _("Cap rounding"), _("Caps:"),
3666                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3667                                                               "tools.calligraphic", "cap_rounding", 0.0,
3668                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3669                                                               0.0, 5.0, 0.01, 0.1,
3670                                                               labels, values, G_N_ELEMENTS(labels),
3671                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3672         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3673         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3674         }
3676         {
3677         /* Tremor */
3678             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3679         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3680         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3681                                                               _("Stroke Tremor"), _("Tremor:"),
3682                                                               _("Increase to make strokes rugged and trembling"),
3683                                                               "tools.calligraphic", "tremor", 0.0,
3684                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3685                                                               0.0, 1.0, 0.01, 0.1,
3686                                                               labels, values, G_N_ELEMENTS(labels),
3687                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3689         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3690         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3691         }
3693         {
3694         /* Wiggle */
3695         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3696         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3697         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3698                                                               _("Pen Wiggle"), _("Wiggle:"),
3699                                                               _("Increase to make the pen waver and wiggle"),
3700                                                               "tools.calligraphic", "wiggle", 0.0,
3701                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3702                                                               0.0, 1.0, 0.01, 0.1,
3703                                                               labels, values, G_N_ELEMENTS(labels),
3704                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3705         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3706         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3707         }
3709         {
3710         /* Mass */
3711             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3712         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3713         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3714                                                               _("Pen Mass"), _("Mass:"),
3715                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3716                                                               "tools.calligraphic", "mass", 0.02,
3717                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3718                                                               0.0, 1.0, 0.01, 0.1,
3719                                                               labels, values, G_N_ELEMENTS(labels),
3720                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3721         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3722         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3723         }
3726         /* Trace Background button */
3727         {
3728             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3729                                                           _("Trace Background"),
3730                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3731                                                           "trace_background",
3732                                                           Inkscape::ICON_SIZE_DECORATION );
3733             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3734             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3735             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3736             g_object_set_data( holder, "tracebackground", act );
3737         }
3739         /* Use Pressure button */
3740         {
3741             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3742                                                           _("Pressure"),
3743                                                           _("Use the pressure of the input device to alter the width of the pen"),
3744                                                           "use_pressure",
3745                                                           Inkscape::ICON_SIZE_DECORATION );
3746             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3747             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3748             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3749             g_object_set_data( holder, "usepressure", act );
3750         }
3752         /* Use Tilt button */
3753         {
3754             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3755                                                           _("Tilt"),
3756                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3757                                                           "use_tilt",
3758                                                           Inkscape::ICON_SIZE_DECORATION );
3759             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3760             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3761             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3762             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3763             g_object_set_data( holder, "usetilt", act );
3764         }
3766         /*calligraphic profile */
3767         {
3768             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3769             gchar *pref_path;
3772             GtkTreeIter iter;
3773             gtk_list_store_append( model, &iter );
3774             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3776             //TODO: switch back to prefs API
3777             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3778             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3779             int ii=1;
3780             while (child_repr) {
3781                 GtkTreeIter iter;
3782                 char *preset_name = (char *) child_repr->attribute("name");
3783                 gtk_list_store_append( model, &iter );                
3784                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
3785                 child_repr = sp_repr_next(child_repr); 
3786             }
3788             pref_path = NULL;
3789             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
3790             ege_select_one_action_set_appearance( act1, "compact" );
3791             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3792             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3793             g_object_set_data( holder, "profile_selector", act1 );
3794         
3795         }
3797         /*Save or delete calligraphic profile */
3798         {
3799             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3800                                              _("Defaults"),
3801                                              _("Save current settings as new profile"),
3802                                              GTK_STOCK_SAVE );
3803             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3806             gtk_action_group_add_action( mainActions, act );
3807             gtk_action_set_sensitive( act, TRUE );
3808             g_object_set_data( holder, "profile_save_delete", act );
3809         }
3810     }
3814 //########################
3815 //##    Circle / Arc    ##
3816 //########################
3818 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3820     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3821     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3823     if (v1 == 0 && v2 == 0) {
3824         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3825             gtk_action_set_sensitive( ocb, FALSE );
3826             gtk_action_set_sensitive( make_whole, FALSE );
3827         }
3828     } else {
3829         gtk_action_set_sensitive( ocb, TRUE );
3830         gtk_action_set_sensitive( make_whole, TRUE );
3831     }
3834 static void
3835 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3837     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3839     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3840         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3841     }
3843     // quit if run by the attr_changed listener
3844     if (g_object_get_data( tbl, "freeze" )) {
3845         return;
3846     }
3848     // in turn, prevent listener from responding
3849     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3851     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3853     bool modmade = false;
3854     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3855          items != NULL;
3856          items = items->next)
3857     {
3858         SPItem *item = SP_ITEM(items->data);
3860         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3862             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3863             SPArc *arc = SP_ARC(item);
3865             if (!strcmp(value_name, "start"))
3866                 ge->start = (adj->value * M_PI)/ 180;
3867             else
3868                 ge->end = (adj->value * M_PI)/ 180;
3870             sp_genericellipse_normalize(ge);
3871             ((SPObject *)arc)->updateRepr();
3872             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3874             modmade = true;
3875         }
3876     }
3878     g_free(namespaced_name);
3880     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3882     sp_arctb_sensitivize( tbl, adj->value, other->value );
3884     if (modmade) {
3885         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3886                                    _("Arc: Change start/end"));
3887     }
3889     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3893 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3895     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3898 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3900     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3904 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3906     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3907     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
3908     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3909         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
3910     }
3912     // only take action if run by the attr_changed listener
3913     if (!g_object_get_data( tbl, "freeze" )) {
3914         // in turn, prevent listener from responding
3915         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3917         if ( eraserMode != 0 ) {
3918         } else {
3919         }
3920         // TODO finish implementation
3922         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3923     }
3926 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3928     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3929     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3930         if ( ege_select_one_action_get_active( act ) != 0 ) {
3931             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3932         } else {
3933             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3934         }
3935     }
3937     // quit if run by the attr_changed listener
3938     if (g_object_get_data( tbl, "freeze" )) {
3939         return;
3940     }
3942     // in turn, prevent listener from responding
3943     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3945     bool modmade = false;
3947     if ( ege_select_one_action_get_active(act) != 0 ) {
3948         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3949              items != NULL;
3950              items = items->next)
3951         {
3952             if (SP_IS_ARC((SPItem *) items->data)) {
3953                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3954                 repr->setAttribute("sodipodi:open", "true");
3955                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3956                 modmade = true;
3957             }
3958         }
3959     } else {
3960         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3961              items != NULL;
3962              items = items->next)
3963         {
3964             if (SP_IS_ARC((SPItem *) items->data))    {
3965                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3966                 repr->setAttribute("sodipodi:open", NULL);
3967                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3968                 modmade = true;
3969             }
3970         }
3971     }
3973     if (modmade) {
3974         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3975                                    _("Arc: Change open/closed"));
3976     }
3978     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3981 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3983     GtkAdjustment *adj;
3984     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3985     gtk_adjustment_set_value(adj, 0.0);
3986     gtk_adjustment_value_changed(adj);
3988     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3989     gtk_adjustment_set_value(adj, 0.0);
3990     gtk_adjustment_value_changed(adj);
3992     spinbutton_defocus( GTK_OBJECT(obj) );
3995 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3996                                       gchar const */*old_value*/, gchar const */*new_value*/,
3997                                       bool /*is_interactive*/, gpointer data)
3999     GObject *tbl = G_OBJECT(data);
4001     // quit if run by the _changed callbacks
4002     if (g_object_get_data( tbl, "freeze" )) {
4003         return;
4004     }
4006     // in turn, prevent callbacks from responding
4007     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4009     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4010     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4012     GtkAdjustment *adj1,*adj2;
4013     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4014     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4015     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4016     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4018     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4020     char const *openstr = NULL;
4021     openstr = repr->attribute("sodipodi:open");
4022     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4024     if (openstr) {
4025         ege_select_one_action_set_active( ocb, 1 );
4026     } else {
4027         ege_select_one_action_set_active( ocb, 0 );
4028     }
4030     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4033 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4034     NULL, /* child_added */
4035     NULL, /* child_removed */
4036     arc_tb_event_attr_changed,
4037     NULL, /* content_changed */
4038     NULL  /* order_changed */
4039 };
4042 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4044     int n_selected = 0;
4045     Inkscape::XML::Node *repr = NULL;
4047     purge_repr_listener( tbl, tbl );
4049     for (GSList const *items = selection->itemList();
4050          items != NULL;
4051          items = items->next)
4052     {
4053         if (SP_IS_ARC((SPItem *) items->data)) {
4054             n_selected++;
4055             repr = SP_OBJECT_REPR((SPItem *) items->data);
4056         }
4057     }
4059     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4061     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4062     if (n_selected == 0) {
4063         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4064     } else if (n_selected == 1) {
4065         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4066         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4068         if (repr) {
4069             g_object_set_data( tbl, "repr", repr );
4070             Inkscape::GC::anchor(repr);
4071             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4072             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4073         }
4074     } else {
4075         // FIXME: implement averaging of all parameters for multiple selected
4076         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4077         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4078         sp_arctb_sensitivize( tbl, 1, 0 );
4079     }
4083 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4085     EgeAdjustmentAction* eact = 0;
4086     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4089     {
4090         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4091         ege_output_action_set_use_markup( act, TRUE );
4092         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4093         g_object_set_data( holder, "mode_action", act );
4094     }
4096     /* Start */
4097     {
4098         eact = create_adjustment_action( "ArcStartAction",
4099                                          _("Start"), _("Start:"),
4100                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4101                                          "tools.shapes.arc", "start", 0.0,
4102                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4103                                          -360.0, 360.0, 1.0, 10.0,
4104                                          0, 0, 0,
4105                                          sp_arctb_start_value_changed);
4106         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4107     }
4109     /* End */
4110     {
4111         eact = create_adjustment_action( "ArcEndAction",
4112                                          _("End"), _("End:"),
4113                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4114                                          "tools.shapes.arc", "end", 0.0,
4115                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4116                                          -360.0, 360.0, 1.0, 10.0,
4117                                          0, 0, 0,
4118                                          sp_arctb_end_value_changed);
4119         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4120     }
4122     /* Segments / Pie checkbox */
4123     {
4124         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4126         GtkTreeIter iter;
4127         gtk_list_store_append( model, &iter );
4128         gtk_list_store_set( model, &iter,
4129                             0, _("Closed arc"),
4130                             1, _("Switch to segment (closed shape with two radii)"),
4131                             2, "circle_closed_arc",
4132                             -1 );
4134         gtk_list_store_append( model, &iter );
4135         gtk_list_store_set( model, &iter,
4136                             0, _("Open Arc"),
4137                             1, _("Switch to arc (unclosed shape)"),
4138                             2, "circle_open_arc",
4139                             -1 );
4141         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4142         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4143         g_object_set_data( holder, "open_action", act );
4145         ege_select_one_action_set_appearance( act, "full" );
4146         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4147         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4148         ege_select_one_action_set_icon_column( act, 2 );
4149         ege_select_one_action_set_icon_size( act, secondarySize );
4150         ege_select_one_action_set_tooltip_column( act, 1  );
4152         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4153         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4154         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4155         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4156     }
4158     /* Make Whole */
4159     {
4160         InkAction* inky = ink_action_new( "ArcResetAction",
4161                                           _("Make whole"),
4162                                           _("Make the shape a whole ellipse, not arc or segment"),
4163                                           "reset_circle",
4164                                           secondarySize );
4165         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4166         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4167         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4168         g_object_set_data( holder, "make_whole", inky );
4169     }
4171     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4172     // sensitivize make whole and open checkbox
4173     {
4174         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4175         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4176         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4177     }
4180     sigc::connection *connection = new sigc::connection(
4181         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4182         );
4183     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4184     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4190 // toggle button callbacks and updaters
4192 //########################
4193 //##      Dropper       ##
4194 //########################
4196 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4197     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4198     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4199     if ( set_action ) {
4200         if ( gtk_toggle_action_get_active( act ) ) {
4201             gtk_action_set_sensitive( set_action, TRUE );
4202         } else {
4203             gtk_action_set_sensitive( set_action, FALSE );
4204         }
4205     }
4207     spinbutton_defocus(GTK_OBJECT(tbl));
4210 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4211     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4212     spinbutton_defocus(GTK_OBJECT(tbl));
4216 /**
4217  * Dropper auxiliary toolbar construction and setup.
4218  *
4219  * TODO: Would like to add swatch of current color.
4220  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4221  *       can drag and drop places. Will provide a nice mixing palette.
4222  */
4223 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4225     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4227     {
4228         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4229         ege_output_action_set_use_markup( act, TRUE );
4230         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4231     }
4233     {
4234         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4235                                                       _("Pick opacity"),
4236                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4237                                                       NULL,
4238                                                       Inkscape::ICON_SIZE_DECORATION );
4239         g_object_set( act, "short_label", _("Pick"), NULL );
4240         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4241         g_object_set_data( holder, "pick_action", act );
4242         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4243         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4244     }
4246     {
4247         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4248                                                       _("Assign opacity"),
4249                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4250                                                       NULL,
4251                                                       Inkscape::ICON_SIZE_DECORATION );
4252         g_object_set( act, "short_label", _("Assign"), NULL );
4253         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4254         g_object_set_data( holder, "set_action", act );
4255         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4256         // make sure it's disabled if we're not picking alpha
4257         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4258         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4259     }
4264 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4266     {
4267         /* Width */
4268         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4269         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4270         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4271                                                               _("Pen Width"), _("Width:"),
4272                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4273                                                               "tools.eraser", "width", 15,
4274                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4275                                                               1, 100, 1.0, 10.0,
4276                                                               labels, values, G_N_ELEMENTS(labels),
4277                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4278         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4279         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4280     }
4282     {
4283         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4285         GtkTreeIter iter;
4286         gtk_list_store_append( model, &iter );
4287         gtk_list_store_set( model, &iter,
4288                             0, _("Delete"),
4289                             1, _("Delete objects touched by the eraser"),
4290                             2, "delete_object",
4291                             -1 );
4293         gtk_list_store_append( model, &iter );
4294         gtk_list_store_set( model, &iter,
4295                             0, _("Cut"),
4296                             1, _("Cut out from objects"),
4297                             2, "difference",
4298                             -1 );
4300         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4301         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4302         g_object_set_data( holder, "eraser_mode_action", act );
4304         ege_select_one_action_set_appearance( act, "full" );
4305         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4306         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4307         ege_select_one_action_set_icon_column( act, 2 );
4308         ege_select_one_action_set_tooltip_column( act, 1  );
4310         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4311         ege_select_one_action_set_active( act, eraserMode );
4312         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4313     }
4314     
4317 //########################
4318 //##    Text Toolbox    ##
4319 //########################
4320 /*
4321 static void
4322 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4324     //Call back for letter sizing spinbutton
4327 static void
4328 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4330     //Call back for line height spinbutton
4333 static void
4334 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4336     //Call back for horizontal kerning spinbutton
4339 static void
4340 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4342     //Call back for vertical kerning spinbutton
4345 static void
4346 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4348     //Call back for letter rotation spinbutton
4349 }*/
4351 namespace {
4353 bool popdown_visible = false;
4354 bool popdown_hasfocus = false;
4356 void
4357 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4359     SPStyle *query =
4360         sp_style_new (SP_ACTIVE_DOCUMENT);
4362     int result_fontspec =
4363         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4365     int result_family =
4366         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4368     int result_style =
4369         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4371     int result_numbers =
4372         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4374     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4376     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4377     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4378     {
4379         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4381         if (repr)
4382         {
4383             sp_style_read_from_repr (query, repr);
4384         }
4385         else
4386         {
4387             return;
4388         }
4389     }
4391     if (query->text)
4392     {
4393         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4394             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4395             gtk_entry_set_text (GTK_ENTRY (entry), "");
4397         } else if (query->text->font_specification.value || query->text->font_family.value) {
4399             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4401             // Get the font that corresponds
4402             Glib::ustring familyName;
4404             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4405             if (font) {
4406                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4407                 font->Unref();
4408                 font = NULL;
4409             }
4411             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4413             Gtk::TreePath path;
4414             try {
4415                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4416             } catch (...) {
4417                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4418                 return;
4419             }
4421             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4422             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4424             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4426             gtk_tree_selection_select_path (tselection, path.gobj());
4427             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4429             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4430         }
4432         //Size
4433         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4434         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4435         g_object_set_data (tbl, "size-block", gpointer(1));
4436         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4437         g_object_set_data (tbl, "size-block", gpointer(0));
4438         free (str);
4440         //Anchor
4441         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4442         {
4443             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4444             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4445             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4446             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4447         }
4448         else
4449         {
4450             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4451             {
4452                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4453                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4454                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4455                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4456             }
4457             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4458             {
4459                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4460                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4461                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4462                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4463             }
4464             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4465             {
4466                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4467                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4468                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4469                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4470             }
4471         }
4473         //Style
4474         {
4475             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4477             gboolean active = gtk_toggle_button_get_active (button);
4478             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4480             if (active != check)
4481             {
4482                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4483                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4484                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4485             }
4486         }
4488         {
4489             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4491             gboolean active = gtk_toggle_button_get_active (button);
4492             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4494             if (active != check)
4495             {
4496                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4497                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4498                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4499             }
4500         }
4502         //Orientation
4503         //locking both buttons, changing one affect all group (both)
4504         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4505         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4507         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4508         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4510         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4511         {
4512             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4513         }
4514         else
4515         {
4516             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4517         }
4518         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4519         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4520     }
4522     sp_style_unref(query);
4525 void
4526 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4528     sp_text_toolbox_selection_changed (selection, tbl);
4531 void
4532 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4534     sp_text_toolbox_selection_changed (NULL, tbl);
4537 void
4538 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4539                                 GObject             *tbl)
4541     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4542     GtkTreeModel *model = 0;
4543     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4544     GtkTreeIter   iter;
4545     char         *family = 0;
4547     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4548     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4550     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4551         return;
4552     }
4554     gtk_tree_model_get (model, &iter, 0, &family, -1);
4556     if (g_object_get_data (G_OBJECT (selection), "block"))
4557     {
4558         gtk_entry_set_text (GTK_ENTRY (entry), family);
4559         return;
4560     }
4562     gtk_entry_set_text (GTK_ENTRY (entry), family);
4564     SPStyle *query =
4565         sp_style_new (SP_ACTIVE_DOCUMENT);
4567     int result_fontspec =
4568         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4570     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4572     SPCSSAttr *css = sp_repr_css_attr_new ();
4575     // First try to get the font spec from the stored value
4576     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4578     if (fontSpec.empty()) {
4579         // Construct a new font specification if it does not yet exist
4580         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4581         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4582         fontFromStyle->Unref();
4583     }
4585     if (!fontSpec.empty()) {
4586         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4587         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4588             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4589             if (font) {
4590                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4592                 // Set all the these just in case they were altered when finding the best
4593                 // match for the new family and old style...
4595                 gchar c[256];
4597                 font->Family(c, 256);
4598                 sp_repr_css_set_property (css, "font-family", c);
4600                 font->Attribute( "weight", c, 256);
4601                 sp_repr_css_set_property (css, "font-weight", c);
4603                 font->Attribute("style", c, 256);
4604                 sp_repr_css_set_property (css, "font-style", c);
4606                 font->Attribute("stretch", c, 256);
4607                 sp_repr_css_set_property (css, "font-stretch", c);
4609                 font->Attribute("variant", c, 256);
4610                 sp_repr_css_set_property (css, "font-variant", c);
4612                 font->Unref();
4613             }
4614         }
4615     }
4617     // If querying returned nothing, set the default style of the tool (for new texts)
4618     if (result_fontspec == QUERY_STYLE_NOTHING)
4619     {
4620         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4621         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4622     }
4623     else
4624     {
4625         sp_desktop_set_style (desktop, css, true, true);
4626     }
4628     sp_style_unref(query);
4630     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4631                                    _("Text: Change font family"));
4632     sp_repr_css_attr_unref (css);
4633     free (family);
4634     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4636     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4639 void
4640 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4641                                        GObject      *tbl)
4643     const char *family = gtk_entry_get_text (entry);
4645     try {
4646         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4647         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4648         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4649         gtk_tree_selection_select_path (selection, path.gobj());
4650         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4651         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4652     } catch (...) {
4653         if (family && strlen (family))
4654         {
4655             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4656         }
4657     }
4660 void
4661 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4662                                    gpointer          data)
4664     if (g_object_get_data (G_OBJECT (button), "block")) return;
4665     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4666     int prop = GPOINTER_TO_INT(data);
4668     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4669     SPCSSAttr *css = sp_repr_css_attr_new ();
4671     switch (prop)
4672     {
4673         case 0:
4674         {
4675             sp_repr_css_set_property (css, "text-anchor", "start");
4676             sp_repr_css_set_property (css, "text-align", "start");
4677             break;
4678         }
4679         case 1:
4680         {
4681             sp_repr_css_set_property (css, "text-anchor", "middle");
4682             sp_repr_css_set_property (css, "text-align", "center");
4683             break;
4684         }
4686         case 2:
4687         {
4688             sp_repr_css_set_property (css, "text-anchor", "end");
4689             sp_repr_css_set_property (css, "text-align", "end");
4690             break;
4691         }
4693         case 3:
4694         {
4695             sp_repr_css_set_property (css, "text-anchor", "start");
4696             sp_repr_css_set_property (css, "text-align", "justify");
4697             break;
4698         }
4699     }
4701     SPStyle *query =
4702         sp_style_new (SP_ACTIVE_DOCUMENT);
4703     int result_numbers =
4704         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4706     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4707     if (result_numbers == QUERY_STYLE_NOTHING)
4708     {
4709         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4710     }
4712     sp_style_unref(query);
4714     sp_desktop_set_style (desktop, css, true, true);
4715     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4716                                    _("Text: Change alignment"));
4717     sp_repr_css_attr_unref (css);
4719     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4722 void
4723 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4724                                gpointer          data)
4726     if (g_object_get_data (G_OBJECT (button), "block")) return;
4728     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4729     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4730     int          prop       = GPOINTER_TO_INT(data);
4731     bool         active     = gtk_toggle_button_get_active (button);
4733     SPStyle *query =
4734         sp_style_new (SP_ACTIVE_DOCUMENT);
4736     int result_fontspec =
4737         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4739     int result_family =
4740         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4742     int result_style =
4743         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4745     int result_numbers =
4746         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4748     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4749     Glib::ustring newFontSpec = "";
4751     if (fontSpec.empty()) {
4752         // Construct a new font specification if it does not yet exist
4753         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4754         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4755         fontFromStyle->Unref();
4756     }
4758     switch (prop)
4759     {
4760         case 0:
4761         {
4762             if (!fontSpec.empty()) {
4763                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4764             }
4765             if (fontSpec != newFontSpec) {
4766                 // Don't even set the bold if the font didn't exist on the system
4767                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4768             }
4769             break;
4770         }
4772         case 1:
4773         {
4774             if (!fontSpec.empty()) {
4775                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4776             }
4777             if (fontSpec != newFontSpec) {
4778                 // Don't even set the italic if the font didn't exist on the system
4779                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4780             }
4781             break;
4782         }
4783     }
4785     if (!newFontSpec.empty()) {
4786         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4787     }
4789     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4790     if (result_fontspec == QUERY_STYLE_NOTHING)
4791     {
4792         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4793     }
4795     sp_style_unref(query);
4797     sp_desktop_set_style (desktop, css, true, true);
4798     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4799                                    _("Text: Change font style"));
4800     sp_repr_css_attr_unref (css);
4802     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4805 void
4806 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4807                                      gpointer         data)
4809     if (g_object_get_data (G_OBJECT (button), "block")) {
4810         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4811         return;
4812     }
4814     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4815     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4816     int          prop       = GPOINTER_TO_INT(data);
4818     switch (prop)
4819     {
4820         case 0:
4821         {
4822             sp_repr_css_set_property (css, "writing-mode", "lr");
4823             break;
4824         }
4826         case 1:
4827         {
4828             sp_repr_css_set_property (css, "writing-mode", "tb");
4829             break;
4830         }
4831     }
4833     SPStyle *query =
4834         sp_style_new (SP_ACTIVE_DOCUMENT);
4835     int result_numbers =
4836         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4838     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4839     if (result_numbers == QUERY_STYLE_NOTHING)
4840     {
4841         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4842     }
4844     sp_desktop_set_style (desktop, css, true, true);
4845     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4846                                    _("Text: Change orientation"));
4847     sp_repr_css_attr_unref (css);
4849     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4852 gboolean
4853 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4855     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4856     if (!desktop) return FALSE;
4858     switch (get_group0_keyval (event)) {
4859         case GDK_Escape: // defocus
4860             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4861             sp_text_toolbox_selection_changed (NULL, tbl); // update
4862             return TRUE; // I consumed the event
4863             break;
4864     }
4865     return FALSE;
4868 gboolean
4869 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4871     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4872     if (!desktop) return FALSE;
4874     switch (get_group0_keyval (event)) {
4875         case GDK_KP_Enter:
4876         case GDK_Return:
4877         case GDK_Escape: // defocus
4878             gtk_widget_hide (w);
4879             popdown_visible = false;
4880             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4881             return TRUE; // I consumed the event
4882             break;
4883         case GDK_w:
4884         case GDK_W:
4885             if (event->state & GDK_CONTROL_MASK) {
4886                 gtk_widget_hide (w);
4887                 popdown_visible = false;
4888                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4889                 return TRUE; // I consumed the event
4890             }
4891             break;
4892     }
4893     return FALSE;
4897 void
4898 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4899                                GObject     *tbl)
4901     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4903     if (g_object_get_data (tbl, "size-block")) return;
4905     // If this is not from selecting a size in the list (in which case get_active will give the
4906     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4907     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4908     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4909     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4910         return;
4912     gchar *endptr;
4913     gdouble value = -1;
4914     char *text = gtk_combo_box_get_active_text (cbox);
4915     if (text) {
4916         value = g_strtod (text, &endptr);
4917         if (endptr == text) // conversion failed, non-numeric input
4918             value = -1;
4919         free (text);
4920     }
4921     if (value <= 0) {
4922         return; // could not parse value
4923     }
4925     SPCSSAttr *css = sp_repr_css_attr_new ();
4926     Inkscape::CSSOStringStream osfs;
4927     osfs << value;
4928     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4930     SPStyle *query =
4931         sp_style_new (SP_ACTIVE_DOCUMENT);
4932     int result_numbers =
4933         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4935     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4936     if (result_numbers == QUERY_STYLE_NOTHING)
4937     {
4938         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4939     }
4941     sp_style_unref(query);
4943     sp_desktop_set_style (desktop, css, true, true);
4944     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4945                                    _("Text: Change font size"));
4946     sp_repr_css_attr_unref (css);
4948     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4951 gboolean
4952 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4954     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4955     if (!desktop) return FALSE;
4957     if (!g_object_get_data (tbl, "esc-pressed")) {
4958         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4959         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4960         sp_text_toolbox_size_changed (cbox, tbl);
4961         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4962     }
4963     return FALSE; // I consumed the event
4967 gboolean
4968 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4970     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4971     if (!desktop) return FALSE;
4973     switch (get_group0_keyval (event)) {
4974         case GDK_Escape: // defocus
4975             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4976             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4977             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4978             return TRUE; // I consumed the event
4979             break;
4980         case GDK_Return: // defocus
4981         case GDK_KP_Enter:
4982             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4983             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4984             sp_text_toolbox_size_changed (cbox, tbl);
4985             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4986             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4987             return TRUE; // I consumed the event
4988             break;
4989     }
4990     return FALSE;
4993 void
4994 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4995                                          GObject            *tbl)
4997     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4998     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4999     int x, y;
5001     if (!popdown_visible)
5002     {
5003         gdk_window_get_origin (widget->window, &x, &y);
5004         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5005         gtk_widget_show_all (popdown);
5006         //sp_transientize (popdown);
5008         gdk_pointer_grab (widget->window, TRUE,
5009                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5010                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5011                                         GDK_POINTER_MOTION_MASK),
5012                           NULL, NULL, GDK_CURRENT_TIME);
5014         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5016         popdown_visible = true;
5017     }
5018     else
5019     {
5020         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5021         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5022         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5023         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5024         gtk_widget_hide (popdown);
5025         popdown_visible = false;
5026     }
5029 gboolean
5030 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5031                                  GdkEventFocus    */*event*/,
5032                                  GObject          */*tbl*/)
5034     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5035     return FALSE;
5038 gboolean
5039 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5040                                    GdkEventFocus    */*event*/,
5041                                    GObject          */*tbl*/)
5043     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5045     if (popdown_hasfocus) {
5046         gtk_widget_hide (popdown);
5047         popdown_hasfocus = false;
5048         popdown_visible = false;
5049         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5050         return TRUE;
5051     }
5052     return FALSE;
5055 gboolean
5056 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5057                                    GdkEventFocus    */*event*/,
5058                                    GObject          */*tbl*/)
5060     popdown_hasfocus = true;
5061     return TRUE;
5065 void
5066 cell_data_func  (GtkTreeViewColumn */*column*/,
5067                  GtkCellRenderer   *cell,
5068                  GtkTreeModel      *tree_model,
5069                  GtkTreeIter       *iter,
5070                  gpointer           /*data*/)
5072     char        *family,
5073         *family_escaped,
5074         *sample_escaped;
5076     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5078     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5080     family_escaped = g_markup_escape_text (family, -1);
5081     sample_escaped = g_markup_escape_text (sample, -1);
5083     std::stringstream markup;
5084     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5085     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5087     free (family);
5088     free (family_escaped);
5089     free (sample_escaped);
5092 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5093     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5094     if (completion) {
5095         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5096         g_object_unref (completion);
5097     }
5100 GtkWidget*
5101 sp_text_toolbox_new (SPDesktop *desktop)
5103     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
5104     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5106     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5107     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5109     GtkTooltips *tt = gtk_tooltips_new();
5110     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5112     ////////////Family
5113     //Window
5114     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5115     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5117     //Entry
5118     GtkWidget           *entry = gtk_entry_new ();
5119     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5120     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5121     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5122     gtk_entry_completion_set_text_column (completion, 0);
5123     gtk_entry_completion_set_minimum_key_length (completion, 1);
5124     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5125     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5126     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5127     aux_toolbox_space (tbl, 1);
5128     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5129     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5131     //Button
5132     GtkWidget   *button = gtk_button_new ();
5133     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5134     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5136     //Popdown
5137     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5138     GtkWidget           *treeview = gtk_tree_view_new ();
5140     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5141     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5142     gtk_tree_view_column_pack_start (column, cell, FALSE);
5143     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5144     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5145     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5147     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5148     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5149     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5151     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5153     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5154     gtk_container_add (GTK_CONTAINER (sw), treeview);
5156     gtk_container_add (GTK_CONTAINER (window), sw);
5157     gtk_widget_set_size_request (window, 300, 450);
5159     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5160     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5161     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5163     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5165     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5166     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5167     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5169     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5170     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5172     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5173     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5174     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5175     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5176     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5178     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5179     aux_toolbox_space (tbl, 1);
5180     GtkWidget *box = gtk_event_box_new ();
5181     gtk_container_add (GTK_CONTAINER (box), image);
5182     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5183     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5184     GtkTooltips *tooltips = gtk_tooltips_new ();
5185     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5186     gtk_widget_hide (GTK_WIDGET (box));
5187     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5189     ////////////Size
5190     const char *sizes[] = {
5191         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5192         "16", "18", "20", "22", "24", "28",
5193         "32", "36", "40", "48", "56", "64", "72", "144"
5194     };
5196     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5197     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5198     gtk_widget_set_size_request (cbox, 80, -1);
5199     aux_toolbox_space (tbl, 1);
5200     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5201     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5202     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5203     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5204     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5206     //spacer
5207     aux_toolbox_space (tbl, 4);
5208     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5210     ////////////Text anchor
5211     GtkWidget *group   = gtk_radio_button_new (NULL);
5212     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5213     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5215     // left
5216     GtkWidget *rbutton = group;
5217     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5218     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5219     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5221     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5222     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5223     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5224     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5226     // center
5227     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5228     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5229     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5230     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5232     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5233     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5234     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5235     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5237     // right
5238     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5239     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5240     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5241     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5243     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5244     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5245     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5246     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5248     // fill
5249     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5250     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5251     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5252     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5254     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5255     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5256     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5257     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5259     aux_toolbox_space (tbl, 1);
5260     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5262     //spacer
5263     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5265     ////////////Text style
5266     row = gtk_hbox_new (FALSE, 4);
5268     // bold
5269     rbutton = gtk_toggle_button_new ();
5270     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5271     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5272     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5273     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5275     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5276     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5277     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5279     // italic
5280     rbutton = gtk_toggle_button_new ();
5281     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5282     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5283     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5284     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5286     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5287     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5288     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5290     aux_toolbox_space (tbl, 1);
5291     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5293     //spacer
5294     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5296     ////////////Text orientation
5297     group   = gtk_radio_button_new (NULL);
5298     row     = gtk_hbox_new (FALSE, 4);
5299     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5301     // horizontal
5302     rbutton = group;
5303     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5304     gtk_container_add           (GTK_CONTAINER (rbutton),
5305                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5306     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5307     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5309     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5310     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5311     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5313     // vertical
5314     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5315     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5316     gtk_container_add           (GTK_CONTAINER (rbutton),
5317                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5318     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5319     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5321     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5322     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5323     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5324     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5327     //watch selection
5328     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5330     sigc::connection *c_selection_changed =
5331         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5332                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5333     pool->add_connection ("selection-changed", c_selection_changed);
5335     sigc::connection *c_selection_modified =
5336         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5337                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5338     pool->add_connection ("selection-modified", c_selection_modified);
5340     sigc::connection *c_subselection_changed =
5341         new sigc::connection (desktop->connectToolSubselectionChanged
5342                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5343     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5345     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5348     gtk_widget_show_all (tbl);
5349     return tbl;
5351 } // end of sp_text_toolbox_new()
5353 }//<unnamed> namespace
5356 //#########################
5357 //##      Connector      ##
5358 //#########################
5360 static void sp_connector_path_set_avoid(void)
5362     cc_selection_set_avoid(true);
5366 static void sp_connector_path_set_ignore(void)
5368     cc_selection_set_avoid(false);
5373 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5375     // quit if run by the _changed callbacks
5376     if (g_object_get_data( tbl, "freeze" )) {
5377         return;
5378     }
5380     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5381     SPDocument *doc = sp_desktop_document(desktop);
5383     if (!sp_document_get_undo_sensitive(doc))
5384     {
5385         return;
5386     }
5388     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5390     if ( repr->attribute("inkscape:connector-spacing") ) {
5391         gdouble priorValue = gtk_adjustment_get_value(adj);
5392         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5393         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5394             return;
5395         }
5396     } else if ( adj->value == defaultConnSpacing ) {
5397         return;
5398     }
5400     // in turn, prevent callbacks from responding
5401     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5403     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5404     SP_OBJECT(desktop->namedview)->updateRepr();
5406     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5407     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5408         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5409         NR::Matrix m = NR::identity();
5410         avoid_item_move(&m, item);
5411     }
5413     if (items) {
5414         g_slist_free(items);
5415     }
5417     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5418             _("Change connector spacing"));
5420     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5422     spinbutton_defocus(GTK_OBJECT(tbl));
5425 static void sp_connector_graph_layout(void)
5427     if (!SP_ACTIVE_DESKTOP) return;
5429     // hack for clones, see comment in align-and-distribute.cpp
5430     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5431     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5433     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5435     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5437     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5440 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5442     if ( gtk_toggle_action_get_active( act ) ) {
5443         prefs_set_string_attribute("tools.connector", "directedlayout",
5444                 "true");
5445     } else {
5446         prefs_set_string_attribute("tools.connector", "directedlayout",
5447                 "false");
5448     }
5451 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5453     if ( gtk_toggle_action_get_active( act ) ) {
5454         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5455                 "true");
5456     } else {
5457         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5458                 "false");
5459     }
5463 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5465     prefs_set_double_attribute("tools.connector", "length", adj->value);
5466     spinbutton_defocus(GTK_OBJECT(tbl));
5469 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5470                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5471                                             bool /*is_interactive*/, gpointer data)
5473     GtkWidget *tbl = GTK_WIDGET(data);
5475     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5476         return;
5477     }
5478     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5479         return;
5480     }
5482     GtkAdjustment *adj = (GtkAdjustment*)
5483             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5484     gdouble spacing = defaultConnSpacing;
5485     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5487     gtk_adjustment_set_value(adj, spacing);
5491 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5492     NULL, /* child_added */
5493     NULL, /* child_removed */
5494     connector_tb_event_attr_changed,
5495     NULL, /* content_changed */
5496     NULL  /* order_changed */
5497 };
5500 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5502     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5504     {
5505         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5506                                           _("Avoid"),
5507                                           _("Make connectors avoid selected objects"),
5508                                           "connector_avoid",
5509                                           secondarySize );
5510         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5511         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5512     }
5514     {
5515         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5516                                           _("Ignore"),
5517                                           _("Make connectors ignore selected objects"),
5518                                           "connector_ignore",
5519                                           secondarySize );
5520         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5521         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5522     }
5524     EgeAdjustmentAction* eact = 0;
5526     // Spacing spinbox
5527     eact = create_adjustment_action( "ConnectorSpacingAction",
5528                                      _("Connector Spacing"), _("Spacing:"),
5529                                      _("The amount of space left around objects by auto-routing connectors"),
5530                                      "tools.connector", "spacing", defaultConnSpacing,
5531                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5532                                      0, 100, 1.0, 10.0,
5533                                      0, 0, 0,
5534                                      connector_spacing_changed, 1, 0 );
5535     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5537     // Graph (connector network) layout
5538     {
5539         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5540                                           _("Graph"),
5541                                           _("Nicely arrange selected connector network"),
5542                                           "graph_layout",
5543                                           secondarySize );
5544         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5545         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5546     }
5548     // Default connector length spinbox
5549     eact = create_adjustment_action( "ConnectorLengthAction",
5550                                      _("Connector Length"), _("Length:"),
5551                                      _("Ideal length for connectors when layout is applied"),
5552                                      "tools.connector", "length", 100,
5553                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5554                                      10, 1000, 10.0, 100.0,
5555                                      0, 0, 0,
5556                                      connector_length_changed, 1, 0 );
5557     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5560     // Directed edges toggle button
5561     {
5562         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5563                                                       _("Downwards"),
5564                                                       _("Make connectors with end-markers (arrows) point downwards"),
5565                                                       "directed_graph",
5566                                                       Inkscape::ICON_SIZE_DECORATION );
5567         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5569         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5570         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5571                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5573         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5574     }
5576     // Avoid overlaps toggle button
5577     {
5578         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5579                                                       _("Remove overlaps"),
5580                                                       _("Do not allow overlapping shapes"),
5581                                                       "remove_overlaps",
5582                                                       Inkscape::ICON_SIZE_DECORATION );
5583         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5585         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5586         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5587                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5589         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5590     }
5592     // Code to watch for changes to the connector-spacing attribute in
5593     // the XML.
5594     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5595     g_assert(repr != NULL);
5597     purge_repr_listener( holder, holder );
5599     if (repr) {
5600         g_object_set_data( holder, "repr", repr );
5601         Inkscape::GC::anchor(repr);
5602         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5603         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5604     }
5605 } // end of sp_connector_toolbox_prep()
5608 //#########################
5609 //##     Paintbucket     ##
5610 //#########################
5612 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5614     gint channels = ege_select_one_action_get_active( act );
5615     flood_channels_set_channels( channels );
5618 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5620     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5623 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5625     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5628 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5630     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5631     SPUnit const *unit = tracker->getActiveUnit();
5633     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5635     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5638 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5640     // FIXME: make defaults settable via Inkscape Options
5641     struct KeyValue {
5642         char const *key;
5643         double value;
5644     } const key_values[] = {
5645         {"threshold", 15},
5646         {"offset", 0.0}
5647     };
5649     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5650         KeyValue const &kv = key_values[i];
5651         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5652         if ( adj ) {
5653             gtk_adjustment_set_value(adj, kv.value);
5654         }
5655     }
5657     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5658     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5659     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5660     ege_select_one_action_set_active( autogap_action, 0 );
5663 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5665     EgeAdjustmentAction* eact = 0;
5667     {
5668         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5670         GList* items = 0;
5671         gint count = 0;
5672         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5673         {
5674             GtkTreeIter iter;
5675             gtk_list_store_append( model, &iter );
5676             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5677             count++;
5678         }
5679         g_list_free( items );
5680         items = 0;
5681         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5682         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5683         ege_select_one_action_set_appearance( act1, "compact" );
5684         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5685         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5686         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5687         g_object_set_data( holder, "channels_action", act1 );
5688     }
5690     // Spacing spinbox
5691     {
5692         eact = create_adjustment_action(
5693             "ThresholdAction",
5694             _("Fill Threshold"), _("Threshold:"),
5695             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5696             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5697             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5698             0, 0, 0,
5699             paintbucket_threshold_changed, 1, 0 );
5701         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5702     }
5704     // Create the units menu.
5705     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5706     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5707     if (stored_unit)
5708         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5709     g_object_set_data( holder, "tracker", tracker );
5710     {
5711         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5712         gtk_action_group_add_action( mainActions, act );
5713     }
5715     // Offset spinbox
5716     {
5717         eact = create_adjustment_action(
5718             "OffsetAction",
5719             _("Grow/shrink by"), _("Grow/shrink by:"),
5720             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5721             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5722             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5723             0, 0, 0,
5724             paintbucket_offset_changed, 1, 2);
5725         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5727         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5728     }
5730     /* Auto Gap */
5731     {
5732         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5734         GList* items = 0;
5735         gint count = 0;
5736         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5737         {
5738             GtkTreeIter iter;
5739             gtk_list_store_append( model, &iter );
5740             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5741             count++;
5742         }
5743         g_list_free( items );
5744         items = 0;
5745         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5746         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5747         ege_select_one_action_set_appearance( act2, "compact" );
5748         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5749         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5750         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5751         g_object_set_data( holder, "autogap_action", act2 );
5752     }
5754     /* Reset */
5755     {
5756         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5757                                           _("Defaults"),
5758                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5759                                           GTK_STOCK_CLEAR );
5760         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5761         gtk_action_group_add_action( mainActions, act );
5762         gtk_action_set_sensitive( act, TRUE );
5763     }
5767 /*
5768   Local Variables:
5769   mode:c++
5770   c-file-style:"stroustrup"
5771   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5772   indent-tabs-mode:nil
5773   fill-column:99
5774   End:
5775 */
5776 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :