Code

reorder widgets for better logical grouping
[inkscape.git] / src / widgets / toolbox.cpp
1 #define __SP_MAINTOOLBOX_C__
3 /** \file
4  * Controls bars for some of Inkscape's tools
5  * (for some tools, they are in their own files)
6  */
8 /*
9 *
10 * Authors:
11 *   MenTaLguY <mental@rydia.net>
12 *   Lauris Kaplinski <lauris@kaplinski.com>
13 *   bulia byak <buliabyak@users.sf.net>
14 *   Frank Felfe <innerspace@iname.com>
15 *   John Cliff <simarilius@yahoo.com>
16 *   David Turner <novalis@gnu.org>
17 *   Josh Andler <scislac@scislac.com>
18 *   Jon A. Cruz <jon@joncruz.org>
19 *
20 * Copyright (C) 2004 David Turner
21 * Copyright (C) 2003 MenTaLguY
22 * Copyright (C) 1999-2006 authors
23 * Copyright (C) 2001-2002 Ximian, Inc.
24 *
25 * Released under GNU GPL, read the file 'COPYING' for more information
26 */
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <gtkmm.h>
33 #include <gtk/gtk.h>
34 #include <iostream>
35 #include <sstream>
37 #include "widgets/button.h"
38 #include "widgets/widget-sizes.h"
39 #include "widgets/spw-utilities.h"
40 #include "widgets/spinbutton-events.h"
41 #include "dialogs/text-edit.h"
43 #include "ui/widget/style-swatch.h"
45 #include "prefs-utils.h"
46 #include "verbs.h"
47 #include "sp-namedview.h"
48 #include "desktop.h"
49 #include "desktop-handles.h"
50 #include "xml/repr.h"
51 #include "xml/node-event-vector.h"
52 #include <glibmm/i18n.h>
53 #include "helper/unit-menu.h"
54 #include "helper/units.h"
56 #include "inkscape.h"
57 #include "conn-avoid-ref.h"
60 #include "select-toolbar.h"
61 #include "gradient-toolbar.h"
63 #include "connector-context.h"
64 #include "node-context.h"
65 #include "shape-editor.h"
66 #include "tweak-context.h"
67 #include "sp-rect.h"
68 #include "box3d.h"
69 #include "box3d-context.h"
70 #include "sp-star.h"
71 #include "sp-spiral.h"
72 #include "sp-ellipse.h"
73 #include "sp-text.h"
74 #include "sp-flowtext.h"
75 #include "style.h"
76 #include "selection.h"
77 #include "document-private.h"
78 #include "desktop-style.h"
79 #include "../libnrtype/font-lister.h"
80 #include "../connection-pool.h"
81 #include "../prefs-utils.h"
82 #include "../inkscape-stock.h"
83 #include "icon.h"
84 #include "graphlayout/graphlayout.h"
86 #include "mod360.h"
88 #include "toolbox.h"
90 #include "flood-context.h"
92 #include "ink-action.h"
93 #include "ege-adjustment-action.h"
94 #include "ege-output-action.h"
95 #include "ege-select-one-action.h"
96 #include "helper/unit-tracker.h"
98 using Inkscape::UnitTracker;
100 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
101 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
103 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
116 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
122 static struct {
123     gchar const *type_name;
124     gchar const *data_name;
125     sp_verb_t verb;
126     sp_verb_t doubleclick_verb;
127 } const tools[] = {
128     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
129     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
130     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
131     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
132     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
133 //    { "SP3DBoxContext",    "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
134     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
135     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
136     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
137     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
138     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
139     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
140     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
141     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
142     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
143     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
144     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
145     { NULL, NULL, 0, 0 }
146 };
148 static struct {
149     gchar const *type_name;
150     gchar const *data_name;
151     GtkWidget *(*create_func)(SPDesktop *desktop);
152     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
153     gchar const *ui_name;
154     gint swatch_verb_id;
155     gchar const *swatch_tool;
156     gchar const *swatch_tip;
157 } const aux_toolboxes[] = {
158     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
159       SP_VERB_INVALID, 0, 0},
160     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
161       SP_VERB_INVALID, 0, 0},
162     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
163       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
164     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
165       SP_VERB_INVALID, 0, 0},
166     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
167       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
168     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
169       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
170     { "SP3DBoxContext",  "3dbox_toolbox",  0, sp_3dbox_toolbox_prep,             "3DBoxToolbar",
171       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
172     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
173       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
174     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
175       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
176     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
177       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
178     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
179       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
180     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
181       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
182     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
183       SP_VERB_INVALID, 0, 0},
184     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
187       SP_VERB_INVALID, 0, 0},
188     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
189       SP_VERB_INVALID, 0, 0},
190     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
191       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
192     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
193 };
196 static gchar const * ui_descr =
197         "<ui>"
198         "  <toolbar name='SelectToolbar'>"
199         "    <toolitem action='ObjectRotate90CCW' />"
200         "    <toolitem action='ObjectRotate90' />"
201         "    <toolitem action='ObjectFlipHorizontally' />"
202         "    <toolitem action='ObjectFlipVertically' />"
203         "    <separator />"
204         "    <toolitem action='SelectionToBack' />"
205         "    <toolitem action='SelectionLower' />"
206         "    <toolitem action='SelectionRaise' />"
207         "    <toolitem action='SelectionToFront' />"
208         "    <separator />"
209         "    <toolitem action='XAction' />"
210         "    <toolitem action='YAction' />"
211         "    <toolitem action='WidthAction' />"
212         "    <toolitem action='LockAction' />"
213         "    <toolitem action='HeightAction' />"
214         "    <toolitem action='UnitsAction' />"
215         "    <separator />"
216         "    <toolitem action='transform_affect_label' />"
217         "    <toolitem action='transform_stroke' />"
218         "    <toolitem action='transform_corners' />"
219         "    <toolitem action='transform_gradient' />"
220         "    <toolitem action='transform_pattern' />"
221         "  </toolbar>"
223         "  <toolbar name='NodeToolbar'>"
224         "    <toolitem action='NodeInsertAction' />"
225         "    <toolitem action='NodeDeleteAction' />"
226         "    <separator />"
227         "    <toolitem action='NodeJoinAction' />"
228         "    <toolitem action='NodeJoinSegmentAction' />"
229         "    <toolitem action='NodeDeleteSegmentAction' />"
230         "    <toolitem action='NodeBreakAction' />"
231         "    <separator />"
232         "    <toolitem action='NodeCuspAction' />"
233         "    <toolitem action='NodeSmoothAction' />"
234         "    <toolitem action='NodeSymmetricAction' />"
235         "    <separator />"
236         "    <toolitem action='NodeLineAction' />"
237         "    <toolitem action='NodeCurveAction' />"
238         "    <separator />"
239         "    <toolitem action='ObjectToPath' />"
240         "    <toolitem action='StrokeToPath' />"
241         "    <separator />"
242         "    <toolitem action='NodesShowHandlesAction' />"
243         "  </toolbar>"
245         "  <toolbar name='TweakToolbar'>"
246         "    <toolitem action='TweakWidthAction' />"
247         "    <separator />"
248         "    <toolitem action='TweakForceAction' />"
249         "    <toolitem action='TweakPressureAction' />"
250         "    <separator />"
251         "    <toolitem action='TweakModeLabel' />"
252         "    <toolitem action='TweakModeAction' />"
253         "    <separator />"
254         "    <toolitem action='TweakChannelsLabel' />"
255         "    <toolitem action='TweakDoH' />"
256         "    <toolitem action='TweakDoS' />"
257         "    <toolitem action='TweakDoL' />"
258         "    <toolitem action='TweakDoO' />"
259         "    <separator />"
260         "    <toolitem action='TweakFidelityAction' />"
261         "  </toolbar>"
263         "  <toolbar name='ZoomToolbar'>"
264         "    <toolitem action='ZoomIn' />"
265         "    <toolitem action='ZoomOut' />"
266         "    <separator />"
267         "    <toolitem action='ZoomSelection' />"
268         "    <toolitem action='ZoomDrawing' />"
269         "    <toolitem action='ZoomPage' />"
270         "    <toolitem action='ZoomPageWidth' />"
271         "    <separator />"
272         "    <toolitem action='ZoomPrev' />"
273         "    <toolitem action='ZoomNext' />"
274         "    <separator />"
275         "    <toolitem action='Zoom1:0' />"
276         "    <toolitem action='Zoom1:2' />"
277         "    <toolitem action='Zoom2:1' />"
278         "  </toolbar>"
280         "  <toolbar name='StarToolbar'>"
281         "    <separator />"
282         "    <toolitem action='StarStateAction' />"
283         "    <separator />"
284         "    <toolitem action='FlatAction' />"
285         "    <separator />"
286         "    <toolitem action='MagnitudeAction' />"
287         "    <toolitem action='SpokeAction' />"
288         "    <toolitem action='RoundednessAction' />"
289         "    <toolitem action='RandomizationAction' />"
290         "    <separator />"
291         "    <toolitem action='StarResetAction' />"
292         "  </toolbar>"
294         "  <toolbar name='RectToolbar'>"
295         "    <toolitem action='RectStateAction' />"
296         "    <toolitem action='RectWidthAction' />"
297         "    <toolitem action='RectHeightAction' />"
298         "    <toolitem action='RadiusXAction' />"
299         "    <toolitem action='RadiusYAction' />"
300         "    <toolitem action='RectUnitsAction' />"
301         "    <separator />"
302         "    <toolitem action='RectResetAction' />"
303         "  </toolbar>"
305         "  <toolbar name='3DBoxToolbar'>"
306         "    <toolitem action='3DBoxPosAngleXAction' />"
307         "    <toolitem action='3DBoxVPXAction' />"
308         "    <separator />"
309         "    <toolitem action='3DBoxPosAngleYAction' />"
310         "    <toolitem action='3DBoxVPYAction' />"
311         "    <separator />"
312         "    <toolitem action='3DBoxPosAngleZAction' />"
313         "    <toolitem action='3DBoxVPZAction' />"
314         "    <separator />"
315         "  </toolbar>"
317         "  <toolbar name='SpiralToolbar'>"
318         "    <toolitem action='SpiralStateAction' />"
319         "    <toolitem action='SpiralRevolutionAction' />"
320         "    <toolitem action='SpiralExpansionAction' />"
321         "    <toolitem action='SpiralT0Action' />"
322         "    <separator />"
323         "    <toolitem action='SpiralResetAction' />"
324         "  </toolbar>"
326         "  <toolbar name='PenToolbar'>"
327         "  </toolbar>"
329         "  <toolbar name='PencilToolbar'>"
330         "  </toolbar>"
332         "  <toolbar name='CalligraphyToolbar'>"
333         "    <separator />"
334         "    <toolitem action='CalligraphyWidthAction' />"
335         "    <toolitem action='PressureAction' />"
336         "    <toolitem action='TraceAction' />"
337         "    <toolitem action='ThinningAction' />"
338         "    <separator />"
339         "    <toolitem action='AngleAction' />"
340         "    <toolitem action='TiltAction' />"
341         "    <toolitem action='FixationAction' />"
342         "    <separator />"
343         "    <toolitem action='CapRoundingAction' />"
344         "    <separator />"
345         "    <toolitem action='TremorAction' />"
346         "    <toolitem action='WiggleAction' />"
347         "    <toolitem action='MassAction' />"
348         "    <separator />"
349         "    <toolitem action='CalligraphyResetAction' />"
350         "  </toolbar>"
352         "  <toolbar name='ArcToolbar'>"
353         "    <toolitem action='ArcStateAction' />"
354         "    <separator />"
355         "    <toolitem action='ArcStartAction' />"
356         "    <toolitem action='ArcEndAction' />"
357         "    <separator />"
358         "    <toolitem action='ArcOpenAction' />"
359         "    <separator />"
360         "    <toolitem action='ArcResetAction' />"
361         "    <separator />"
362         "  </toolbar>"
364         "  <toolbar name='PaintbucketToolbar'>"
365         "    <toolitem action='ChannelsAction' />"
366         "    <separator />"
367         "    <toolitem action='ThresholdAction' />"
368         "    <separator />"
369         "    <toolitem action='OffsetAction' />"
370         "    <toolitem action='PaintbucketUnitsAction' />"
371         "    <separator />"
372         "    <toolitem action='AutoGapAction' />"
373         "    <separator />"
374         "    <toolitem action='PaintbucketResetAction' />"
375         "  </toolbar>"
377         "  <toolbar name='DropperToolbar'>"
378         "    <toolitem action='DropperPickAlphaAction' />"
379         "    <toolitem action='DropperSetAlphaAction' />"
380         "  </toolbar>"
382         "  <toolbar name='ConnectorToolbar'>"
383         "    <toolitem action='ConnectorAvoidAction' />"
384         "    <toolitem action='ConnectorIgnoreAction' />"
385         "    <toolitem action='ConnectorSpacingAction' />"
386         "    <toolitem action='ConnectorGraphAction' />"
387         "    <toolitem action='ConnectorLengthAction' />"
388         "    <toolitem action='ConnectorDirectedAction' />"
389         "    <toolitem action='ConnectorOverlapAction' />"
390         "  </toolbar>"
392         "</ui>"
395 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
397 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
399 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
400 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
402 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
403 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
405 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
406 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
408 /* Global text entry widgets necessary for update */
409 /* GtkWidget *dropper_rgb_entry,
410           *dropper_opacity_entry ; */
411 // should be made a private member once this is converted to class
413 static void delete_connection(GObject *obj, sigc::connection *connection) {
414     connection->disconnect();
415     delete connection;
418 static void purge_repr_listener( GObject* obj, GObject* tbl )
420     (void)obj;
421     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
422     if (oldrepr) { // remove old listener
423         sp_repr_remove_listener_by_data(oldrepr, tbl);
424         Inkscape::GC::release(oldrepr);
425         oldrepr = 0;
426         g_object_set_data( tbl, "repr", NULL );
427     }
430 GtkWidget *
431 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
432                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
433                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
435     SPAction *action = verb->get_action(view);
436     if (!action) return NULL;
438     SPAction *doubleclick_action;
439     if (doubleclick_verb)
440         doubleclick_action = doubleclick_verb->get_action(view);
441     else
442         doubleclick_action = NULL;
444     /* fixme: Handle sensitive/unsensitive */
445     /* fixme: Implement sp_button_new_from_action */
446     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
447     gtk_widget_show(b);
448     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
450     return b;
453 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
454                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
456     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
459 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
460                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
462     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
466 static void trigger_sp_action( GtkAction* act, gpointer user_data )
468     SPAction* targetAction = SP_ACTION(user_data);
469     if ( targetAction ) {
470         sp_action_perform( targetAction, NULL );
471     }
474 static void sp_action_action_set_sensitive (SPAction *action, unsigned int sensitive, void *data)
476     if ( data ) {
477         GtkAction* act = GTK_ACTION(data);
478         gtk_action_set_sensitive( act, sensitive );
479     }
482 static SPActionEventVector action_event_vector = {
483     {NULL},
484     NULL,
485     NULL,
486     sp_action_action_set_sensitive,
487     NULL,
488     NULL
489 };
491 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
493     GtkAction* act = 0;
495     SPAction* targetAction = verb->get_action(view);
496     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
497     act = GTK_ACTION(inky);
498     gtk_action_set_sensitive( act, targetAction->sensitive );
500     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
502     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
503     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
505     return act;
508 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
510     Inkscape::UI::View::View *view = desktop;
511     gint verbsToUse[] = {
512         // disabled until we have icons for them:
513         //find
514         //SP_VERB_EDIT_TILE,
515         //SP_VERB_EDIT_UNTILE,
516         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
517         SP_VERB_DIALOG_DISPLAY,
518         SP_VERB_DIALOG_FILL_STROKE,
519         SP_VERB_DIALOG_NAMEDVIEW,
520         SP_VERB_DIALOG_TEXT,
521         SP_VERB_DIALOG_XML_EDITOR,
522         SP_VERB_EDIT_CLONE,
523         SP_VERB_EDIT_COPY,
524         SP_VERB_EDIT_CUT,
525         SP_VERB_EDIT_DUPLICATE,
526         SP_VERB_EDIT_PASTE,
527         SP_VERB_EDIT_REDO,
528         SP_VERB_EDIT_UNDO,
529         SP_VERB_EDIT_UNLINK_CLONE,
530         SP_VERB_FILE_EXPORT,
531         SP_VERB_FILE_IMPORT,
532         SP_VERB_FILE_NEW,
533         SP_VERB_FILE_OPEN,
534         SP_VERB_FILE_PRINT,
535         SP_VERB_FILE_SAVE,
536         SP_VERB_OBJECT_TO_CURVE,
537         SP_VERB_SELECTION_GROUP,
538         SP_VERB_SELECTION_OUTLINE,
539         SP_VERB_SELECTION_UNGROUP,
540         SP_VERB_ZOOM_1_1,
541         SP_VERB_ZOOM_1_2,
542         SP_VERB_ZOOM_2_1,
543         SP_VERB_ZOOM_DRAWING,
544         SP_VERB_ZOOM_IN,
545         SP_VERB_ZOOM_NEXT,
546         SP_VERB_ZOOM_OUT,
547         SP_VERB_ZOOM_PAGE,
548         SP_VERB_ZOOM_PAGE_WIDTH,
549         SP_VERB_ZOOM_PREV,
550         SP_VERB_ZOOM_SELECTION,
551     };
553     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
554     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
556     static std::map<SPDesktop*, GtkActionGroup*> groups;
557     GtkActionGroup* mainActions = 0;
558     if ( groups.find(desktop) != groups.end() ) {
559         mainActions = groups[desktop];
560     }
562     if ( !mainActions ) {
563         mainActions = gtk_action_group_new("main");
564         groups[desktop] = mainActions;
565     }
567     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
568         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
569         if ( verb ) {
570             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
571                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
572                 gtk_action_group_add_action( mainActions, act );
573             }
574         }
575     }
577     return mainActions;
581 GtkWidget *
582 sp_tool_toolbox_new()
584     GtkTooltips *tt = gtk_tooltips_new();
585     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
587     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
588     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
590     gtk_widget_set_sensitive(tb, FALSE);
592     GtkWidget *hb = gtk_handle_box_new();
593     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
594     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
595     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
597     gtk_container_add(GTK_CONTAINER(hb), tb);
598     gtk_widget_show(GTK_WIDGET(tb));
600     sigc::connection* conn = new sigc::connection;
601     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
603     return hb;
606 static void
607 aux_toolbox_attached(GtkHandleBox *toolbox, GtkWidget *child)
609     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
610     gtk_widget_queue_resize(child);
613 static void
614 aux_toolbox_detached(GtkHandleBox *toolbox, GtkWidget *child)
616     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
617     gtk_widget_queue_resize(child);
620 GtkWidget *
621 sp_aux_toolbox_new()
623     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
625     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
626     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
627     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
628     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
629     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
631     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
632     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
634     gtk_widget_set_sensitive(tb, FALSE);
636     GtkWidget *hb = gtk_handle_box_new();
637     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
638     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
639     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
641     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
642     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
644     gtk_container_add(GTK_CONTAINER(hb), tb);
645     gtk_widget_show(GTK_WIDGET(tb));
647     sigc::connection* conn = new sigc::connection;
648     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
650     return hb;
653 //####################################
654 //# Commands Bar
655 //####################################
657 GtkWidget *
658 sp_commands_toolbox_new()
660     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
662     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
663     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
664     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
665     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
666     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
668     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
669     gtk_widget_set_sensitive(tb, FALSE);
671     GtkWidget *hb = gtk_handle_box_new();
672     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
673     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
674     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
676     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
677     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
679     gtk_container_add(GTK_CONTAINER(hb), tb);
680     gtk_widget_show(GTK_WIDGET(tb));
682     sigc::connection* conn = new sigc::connection;
683     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
685     return hb;
689 //####################################
690 //# node editing callbacks
691 //####################################
693 /**
694  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
695  */
696 static ShapeEditor *get_current_shape_editor()
698     if (!SP_ACTIVE_DESKTOP) {
699         return NULL;
700     }
702     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
704     if (!SP_IS_NODE_CONTEXT(event_context)) {
705         return NULL;
706     }
708     return SP_NODE_CONTEXT(event_context)->shape_editor;
712 void
713 sp_node_path_edit_add(void)
715     ShapeEditor *shape_editor = get_current_shape_editor();
716     if (shape_editor) shape_editor->add_node();
719 void
720 sp_node_path_edit_delete(void)
722     ShapeEditor *shape_editor = get_current_shape_editor();
723     if (shape_editor) shape_editor->delete_nodes();
726 void
727 sp_node_path_edit_delete_segment(void)
729     ShapeEditor *shape_editor = get_current_shape_editor();
730     if (shape_editor) shape_editor->delete_segment();
733 void
734 sp_node_path_edit_break(void)
736     ShapeEditor *shape_editor = get_current_shape_editor();
737     if (shape_editor) shape_editor->break_at_nodes();
740 void
741 sp_node_path_edit_join(void)
743     ShapeEditor *shape_editor = get_current_shape_editor();
744     if (shape_editor) shape_editor->join_nodes();
747 void
748 sp_node_path_edit_join_segment(void)
750     ShapeEditor *shape_editor = get_current_shape_editor();
751     if (shape_editor) shape_editor->join_segments();
754 void
755 sp_node_path_edit_toline(void)
757     ShapeEditor *shape_editor = get_current_shape_editor();
758     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
761 void
762 sp_node_path_edit_tocurve(void)
764     ShapeEditor *shape_editor = get_current_shape_editor();
765     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
768 void
769 sp_node_path_edit_cusp(void)
771     ShapeEditor *shape_editor = get_current_shape_editor();
772     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
775 void
776 sp_node_path_edit_smooth(void)
778     ShapeEditor *shape_editor = get_current_shape_editor();
779     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
782 void
783 sp_node_path_edit_symmetrical(void)
785     ShapeEditor *shape_editor = get_current_shape_editor();
786     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
789 static void toggle_show_handles (GtkToggleAction *act, gpointer data) {
790     bool show = gtk_toggle_action_get_active( act );
791     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
792     ShapeEditor *shape_editor = get_current_shape_editor();
793     if (shape_editor) shape_editor->show_handles(show);
796 //################################
797 //##    Node Editing Toolbox    ##
798 //################################
800 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
802     {
803         InkAction* inky = ink_action_new( "NodeInsertAction",
804                                           _("Insert"),
805                                           _("Insert new nodes into selected segments"),
806                                           "node_insert",
807                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
808         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
809         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
810     }
812     {
813         InkAction* inky = ink_action_new( "NodeDeleteAction",
814                                           _("Delete"),
815                                           _("Delete selected nodes"),
816                                           "node_delete",
817                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
818         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
819         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
820     }
822     {
823         InkAction* inky = ink_action_new( "NodeJoinAction",
824                                           _("Join"),
825                                           _("Join selected endnodes"),
826                                           "node_join",
827                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
828         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
829         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
830     }
832     {
833         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
834                                           _("Join Segment"),
835                                           _("Join selected endnodes with a new segment"),
836                                           "node_join_segment",
837                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
838         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
839         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
840     }
842     {
843         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
844                                           _("Delete Segment"),
845                                           _("Split path between two non-endpoint nodes"),
846                                           "node_delete_segment",
847                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
848         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
849         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
850     }
852     {
853         InkAction* inky = ink_action_new( "NodeBreakAction",
854                                           _("Node Break"),
855                                           _("Break path at selected nodes"),
856                                           "node_break",
857                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
858         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
859         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
860     }
862     {
863         InkAction* inky = ink_action_new( "NodeCuspAction",
864                                           _("Node Cusp"),
865                                           _("Make selected nodes corner"),
866                                           "node_cusp",
867                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
868         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
869         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
870     }
872     {
873         InkAction* inky = ink_action_new( "NodeSmoothAction",
874                                           _("Node Smooth"),
875                                           _("Make selected nodes smooth"),
876                                           "node_smooth",
877                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
878         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
879         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
880     }
882     {
883         InkAction* inky = ink_action_new( "NodeSymmetricAction",
884                                           _("Node Symmetric"),
885                                           _("Make selected nodes symmetric"),
886                                           "node_symmetric",
887                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
888         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
889         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
890     }
892     {
893         InkAction* inky = ink_action_new( "NodeLineAction",
894                                           _("Node Line"),
895                                           _("Make selected segments lines"),
896                                           "node_line",
897                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
898         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
899         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
900     }
902     {
903         InkAction* inky = ink_action_new( "NodeCurveAction",
904                                           _("Node Curve"),
905                                           _("Make selected segments curves"),
906                                           "node_curve",
907                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
908         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
909         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
910     }
912     {
913         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
914                                                       _("Show Handles"),
915                                                       _("Show the Bezier handles of selected nodes"),
916                                                       "nodes_show_handles",
917                                                       Inkscape::ICON_SIZE_DECORATION );
918         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
919         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
920         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
921     }
923 } // end of sp_node_toolbox_prep()
926 //########################
927 //##    Zoom Toolbox    ##
928 //########################
930 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
932     // no custom GtkAction setup needed
933 } // end of sp_zoom_toolbox_prep()
935 void
936 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
938     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")));
942 void
943 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
945     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")));
948 void
949 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
951     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")));
954 static void
955 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
957     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
958     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
960     if (old_desktop) {
961         GList *children, *iter;
963         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
964         for ( iter = children ; iter ; iter = iter->next ) {
965             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
966         }
967         g_list_free(children);
968     }
970     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
972     if (desktop) {
973         gtk_widget_set_sensitive(toolbox, TRUE);
974         setup_func(toolbox, desktop);
975         update_func(desktop, desktop->event_context, toolbox);
976         *conn = desktop->connectEventContextChanged
977             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
978     } else {
979         gtk_widget_set_sensitive(toolbox, FALSE);
980     }
982 } // end of toolbox_set_desktop()
985 static void
986 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
988     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
989     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
990     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
991         // "toolbox.tools" was not set. Fallback to older value
992         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
994         // Copy the setting forwards
995         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
996     }
997     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
999     for (int i = 0 ; tools[i].type_name ; i++ ) {
1000         GtkWidget *button =
1001             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1002                                                               SP_BUTTON_TYPE_TOGGLE,
1003                                                               Inkscape::Verb::get(tools[i].verb),
1004                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1005                                                               desktop,
1006                                                               tooltips );
1008         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1009                            (gpointer)button );
1010     }
1014 static void
1015 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
1017     gchar const *const tname = ( eventcontext
1018                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1019                                  : NULL );
1020     for (int i = 0 ; tools[i].type_name ; i++ ) {
1021         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1022         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1023     }
1026 static void
1027 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1029     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1030     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1031     GtkUIManager* mgr = gtk_ui_manager_new();
1032     GError* errVal = 0;
1033     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1034     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1036     std::map<std::string, GtkWidget*> dataHolders;
1038     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1039         if ( aux_toolboxes[i].prep_func ) {
1040             // converted to GtkActions and UIManager
1042             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1043             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1044             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1045             dataHolders[aux_toolboxes[i].type_name] = kludge;
1046             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1047         } else {
1049             GtkWidget *sub_toolbox = 0;
1050             if (aux_toolboxes[i].create_func == NULL)
1051                 sub_toolbox = sp_empty_toolbox_new(desktop);
1052             else {
1053                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1054             }
1056             gtk_size_group_add_widget( grouper, sub_toolbox );
1058             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1059             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1061         }
1062     }
1064     // Second pass to create toolbars *after* all GtkActions are created
1065     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1066         if ( aux_toolboxes[i].prep_func ) {
1067             // converted to GtkActions and UIManager
1069             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1071             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1072             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1074             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1075             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1076             g_free( tmp );
1077             tmp = 0;
1079             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1080             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1081             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1082                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1083             }
1084             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1087             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1089             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1090                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1091                 swatch->setDesktop( desktop );
1092                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1093                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1094                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1095                 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 );
1096             }
1098             gtk_widget_show_all( holder );
1099             sp_set_font_size_smaller( holder );
1101             gtk_size_group_add_widget( grouper, holder );
1103             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1104             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1105         }
1106     }
1108     g_object_unref( G_OBJECT(grouper) );
1111 static void
1112 update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1114     gchar const *tname = ( eventcontext
1115                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1116                            : NULL );
1117     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1118         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1119         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1120             gtk_widget_show_all(sub_toolbox);
1121             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1122         } else {
1123             gtk_widget_hide(sub_toolbox);
1124         }
1125     }
1128 static void
1129 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1131     gchar const * descr =
1132         "<ui>"
1133         "  <toolbar name='CommandsToolbar'>"
1134         "    <toolitem action='FileNew' />"
1135         "    <toolitem action='FileOpen' />"
1136         "    <toolitem action='FileSave' />"
1137         "    <toolitem action='FilePrint' />"
1138         "    <separator />"
1139         "    <toolitem action='FileImport' />"
1140         "    <toolitem action='FileExport' />"
1141         "    <separator />"
1142         "    <toolitem action='EditUndo' />"
1143         "    <toolitem action='EditRedo' />"
1144         "    <separator />"
1145         "    <toolitem action='EditCopy' />"
1146         "    <toolitem action='EditCut' />"
1147         "    <toolitem action='EditPaste' />"
1148         "    <separator />"
1149         "    <toolitem action='ZoomSelection' />"
1150         "    <toolitem action='ZoomDrawing' />"
1151         "    <toolitem action='ZoomPage' />"
1152         "    <separator />"
1153         "    <toolitem action='EditDuplicate' />"
1154         "    <toolitem action='EditClone' />"
1155         "    <toolitem action='EditUnlinkClone' />"
1156         "    <separator />"
1157         "    <toolitem action='SelectionGroup' />"
1158         "    <toolitem action='SelectionUnGroup' />"
1159         "    <separator />"
1160         "    <toolitem action='DialogFillStroke' />"
1161         "    <toolitem action='DialogText' />"
1162         "    <toolitem action='DialogXMLEditor' />"
1163         "    <toolitem action='DialogAlignDistribute' />"
1164         "    <separator />"
1165         "    <toolitem action='DialogPreferences' />"
1166         "    <toolitem action='DialogDocumentProperties' />"
1167         "  </toolbar>"
1168         "</ui>";
1169     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1172     GtkUIManager* mgr = gtk_ui_manager_new();
1173     GError* errVal = 0;
1175     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1176     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1178     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1179     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1180         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1181     }
1182     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1183     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1184     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1187     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1190 static void
1191 update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1195 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1197     gtk_widget_show(toolbox_toplevel);
1198     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1200     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1201     if (!shown_toolbox) {
1202         return;
1203     }
1204     gtk_widget_show(toolbox);
1206     // need to show the spacer, or the padding will be off
1207     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1208     gtk_widget_show(spacer);
1210     gtk_widget_show_all(shown_toolbox);
1213 void
1214 aux_toolbox_space(GtkWidget *tb, gint space)
1216     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1219 static GtkWidget *
1220 sp_empty_toolbox_new(SPDesktop *desktop)
1222     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1223     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1224     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1226     gtk_widget_show_all(tbl);
1227     sp_set_font_size_smaller (tbl);
1229     return tbl;
1232 // helper UI functions
1234 GtkWidget *
1235 sp_tb_spinbutton(
1236     gchar *label, gchar const *tooltip,
1237     gchar const *path, gchar const *data, gdouble def,
1238     GtkWidget *us,
1239     GtkWidget *tbl,
1240     gboolean altx, gchar const *altx_mark,
1241     gdouble lower, gdouble upper, gdouble step, gdouble page,
1242     void (*callback)(GtkAdjustment *, GtkWidget *),
1243     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1245     GtkTooltips *tt = gtk_tooltips_new();
1247     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1249     GtkWidget *l = gtk_label_new(label);
1250     gtk_widget_show(l);
1251     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1252     gtk_container_add(GTK_CONTAINER(hb), l);
1254     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1255                                       lower, upper, step, page, page);
1256     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1257     if (us)
1258         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1260     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1261     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1262     if (altx)
1263         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1264     gtk_widget_set_size_request(sb,
1265                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1266                                 AUX_SPINBUTTON_HEIGHT);
1267     gtk_widget_show(sb);
1268     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1269     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1270     gtk_container_add(GTK_CONTAINER(hb), sb);
1271     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1273     return hb;
1276 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1277                                                        gchar const *label, gchar const *tooltip,
1278                                                        gchar const *path, gchar const *data, gdouble def,
1279                                                        GtkWidget *focusTarget,
1280                                                        GtkWidget *us,
1281                                                        GObject *dataKludge,
1282                                                        gboolean altx, gchar const *altx_mark,
1283                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1284                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1285                                                        void (*callback)(GtkAdjustment *, GObject *),
1286                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1288     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
1289                                                              lower, upper, step, page, page ) );
1290     if (us) {
1291         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1292     }
1294     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1296     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1298     if ( (descrCount > 0) && descrLabels && descrValues ) {
1299         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1300     }
1302     if ( focusTarget ) {
1303         ege_adjustment_action_set_focuswidget( act, focusTarget );
1304     }
1306     if ( altx && altx_mark ) {
1307         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1308     }
1310     if ( dataKludge ) {
1311         g_object_set_data( dataKludge, data, adj );
1312     }
1314     // Using a cast just to make sure we pass in the right kind of function pointer
1315     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1317     return act;
1321 #define MODE_LABEL_WIDTH 70
1323 //########################
1324 //##       Star         ##
1325 //########################
1327 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1329     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1331     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1332         // do not remember prefs if this call is initiated by an undo change, because undoing object
1333         // creation sets bogus values to its attributes before it is deleted
1334         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1335     }
1337     // quit if run by the attr_changed listener
1338     if (g_object_get_data( dataKludge, "freeze" )) {
1339         return;
1340     }
1342     // in turn, prevent listener from responding
1343     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1345     bool modmade = false;
1347     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1348     GSList const *items = selection->itemList();
1349     for (; items != NULL; items = items->next) {
1350         if (SP_IS_STAR((SPItem *) items->data)) {
1351             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1352             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1353             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1354                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1355                                     + M_PI / (gint)adj->value));
1356             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1357             modmade = true;
1358         }
1359     }
1360     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1361                                    _("Star: Change number of corners"));
1363     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1366 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1368     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1370     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1371         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1372     }
1374     // quit if run by the attr_changed listener
1375     if (g_object_get_data( dataKludge, "freeze" )) {
1376         return;
1377     }
1379     // in turn, prevent listener from responding
1380     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1382     bool modmade = false;
1383     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1384     GSList const *items = selection->itemList();
1385     for (; items != NULL; items = items->next) {
1386         if (SP_IS_STAR((SPItem *) items->data)) {
1387             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1389             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1390             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1391             if (r2 < r1) {
1392                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1393             } else {
1394                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1395             }
1397             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1398             modmade = true;
1399         }
1400     }
1402     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1403                                    _("Star: Change spoke ratio"));
1405     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1408 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1410     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1411     bool flat = ege_select_one_action_get_active( act ) == 0;
1413     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1414         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1415                                     flat ? "true" : "false" );
1416     }
1418     // quit if run by the attr_changed listener
1419     if (g_object_get_data( dataKludge, "freeze" )) {
1420         return;
1421     }
1423     // in turn, prevent listener from responding
1424     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1426     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1427     GSList const *items = selection->itemList();
1428     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1429     bool modmade = false;
1431     if ( prop_action ) {
1432         gtk_action_set_sensitive( prop_action, !flat );
1433     }
1435     for (; items != NULL; items = items->next) {
1436         if (SP_IS_STAR((SPItem *) items->data)) {
1437             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1438             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1439             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1440             modmade = true;
1441         }
1442     }
1444     if (modmade) {
1445         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1446                          flat ? _("Make polygon") : _("Make star"));
1447     }
1449     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1452 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1454     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1456     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1457         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1458     }
1460     // quit if run by the attr_changed listener
1461     if (g_object_get_data( dataKludge, "freeze" )) {
1462         return;
1463     }
1465     // in turn, prevent listener from responding
1466     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1468     bool modmade = false;
1470     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1471     GSList const *items = selection->itemList();
1472     for (; items != NULL; items = items->next) {
1473         if (SP_IS_STAR((SPItem *) items->data)) {
1474             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1475             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1476             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1477             modmade = true;
1478         }
1479     }
1480     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1481                                    _("Star: Change rounding"));
1483     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1486 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1488     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1490     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1491         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1492     }
1494     // quit if run by the attr_changed listener
1495     if (g_object_get_data( dataKludge, "freeze" )) {
1496         return;
1497     }
1499     // in turn, prevent listener from responding
1500     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1502     bool modmade = false;
1504     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1505     GSList const *items = selection->itemList();
1506     for (; items != NULL; items = items->next) {
1507         if (SP_IS_STAR((SPItem *) items->data)) {
1508             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1509             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1510             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1511             modmade = true;
1512         }
1513     }
1514     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1515                                    _("Star: Change randomization"));
1517     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1521 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1522                                        gchar const *old_value, gchar const *new_value,
1523                                        bool is_interactive, gpointer data)
1525     GtkWidget *tbl = GTK_WIDGET(data);
1527     // quit if run by the _changed callbacks
1528     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1529         return;
1530     }
1532     // in turn, prevent callbacks from responding
1533     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1535     GtkAdjustment *adj = 0;
1537     if (!strcmp(name, "inkscape:randomized")) {
1538         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1539         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1540     } else if (!strcmp(name, "inkscape:rounded")) {
1541         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1542         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1543     } else if (!strcmp(name, "inkscape:flatsided")) {
1544         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1545         char const *flatsides = repr->attribute("inkscape:flatsided");
1546         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1547         if ( flatsides && !strcmp(flatsides,"false") ) {
1548             ege_select_one_action_set_active( flat_action, 1 );
1549             gtk_action_set_sensitive( prop_action, TRUE );
1550         } else {
1551             ege_select_one_action_set_active( flat_action, 0 );
1552             gtk_action_set_sensitive( prop_action, FALSE );
1553         }
1554     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1555         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1556         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1557         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1558         if (r2 < r1) {
1559             gtk_adjustment_set_value(adj, r2/r1);
1560         } else {
1561             gtk_adjustment_set_value(adj, r1/r2);
1562         }
1563     } else if (!strcmp(name, "sodipodi:sides")) {
1564         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1565         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1566     }
1568     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1572 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1574     NULL, /* child_added */
1575     NULL, /* child_removed */
1576     star_tb_event_attr_changed,
1577     NULL, /* content_changed */
1578     NULL  /* order_changed */
1579 };
1582 /**
1583  *  \param selection Should not be NULL.
1584  */
1585 static void
1586 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1588     int n_selected = 0;
1589     Inkscape::XML::Node *repr = NULL;
1591     purge_repr_listener( tbl, tbl );
1593     for (GSList const *items = selection->itemList();
1594          items != NULL;
1595          items = items->next)
1596     {
1597         if (SP_IS_STAR((SPItem *) items->data)) {
1598             n_selected++;
1599             repr = SP_OBJECT_REPR((SPItem *) items->data);
1600         }
1601     }
1603     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1605     if (n_selected == 0) {
1606         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1607     } else if (n_selected == 1) {
1608         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1610         if (repr) {
1611             g_object_set_data( tbl, "repr", repr );
1612             Inkscape::GC::anchor(repr);
1613             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1614             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1615         }
1616     } else {
1617         // FIXME: implement averaging of all parameters for multiple selected stars
1618         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1619         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1620     }
1624 static void sp_stb_defaults( GtkWidget *widget, GObject *dataKludge )
1626     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1627     // callbacks to lump all the changes for all selected objects in one undo step
1629     GtkAdjustment *adj = 0;
1631     // fixme: make settable in prefs!
1632     gint mag = 5;
1633     gdouble prop = 0.5;
1634     gboolean flat = FALSE;
1635     gdouble randomized = 0;
1636     gdouble rounded = 0;
1638     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1639     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1641     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1642     gtk_action_set_sensitive( sb2, !flat );
1644     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1645     gtk_adjustment_set_value(adj, mag);
1646     gtk_adjustment_value_changed(adj);
1648     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1649     gtk_adjustment_set_value(adj, prop);
1650     gtk_adjustment_value_changed(adj);
1652     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1653     gtk_adjustment_set_value(adj, rounded);
1654     gtk_adjustment_value_changed(adj);
1656     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1657     gtk_adjustment_set_value(adj, randomized);
1658     gtk_adjustment_value_changed(adj);
1662 void
1663 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1665     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1666     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1667     GtkWidget *l = gtk_label_new(NULL);
1668     gtk_label_set_markup(GTK_LABEL(l), title);
1669     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1670     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1671     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1675 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1677     {
1678         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1679         ege_output_action_set_use_markup( act, TRUE );
1680         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1681         g_object_set_data( holder, "mode_action", act );
1682     }
1684     {
1685         //EgeAdjustmentAction* calligraphy_angle = 0;
1686         EgeAdjustmentAction* eact = 0;
1687         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1688         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1690         /* Flatsided checkbox */
1691         {
1692             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1694             GtkTreeIter iter;
1695             gtk_list_store_append( model, &iter );
1696             gtk_list_store_set( model, &iter,
1697                                 0, _("Polygon"),
1698                                 1, _("Regular polygon (with one handle) instead of a star"),
1699                                 2, "star_flat",
1700                                 -1 );
1702             gtk_list_store_append( model, &iter );
1703             gtk_list_store_set( model, &iter,
1704                                 0, _("Star"),
1705                                 1, _("Star instead of a regular polygon (with one handle)"),
1706                                 2, "star_angled",
1707                                 -1 );
1709             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
1710             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1711             g_object_set_data( holder, "flat_action", act );
1713             ege_select_one_action_set_appearance( act, "full" );
1714             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1715             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1716             ege_select_one_action_set_icon_column( act, 2 );
1717             ege_select_one_action_set_tooltip_column( act, 1  );
1719             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1720             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1721         }
1723         /* Magnitude */
1724         {
1725         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1726         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1727         eact = create_adjustment_action( "MagnitudeAction",
1728                                          _("Corners:"), _("Number of corners of a polygon or star"),
1729                                          "tools.shapes.star", "magnitude", 3,
1730                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1731                                          3, 1024, 1, 5,
1732                                          labels, values, G_N_ELEMENTS(labels),
1733                                          sp_stb_magnitude_value_changed,
1734                                          1.0, 0 );
1735         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1736         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1737         }
1739         /* Spoke ratio */
1740         {
1741         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1742         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1743         eact = create_adjustment_action( "SpokeAction",
1744                                          _("Spoke ratio:"),
1745                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1746                                          // Base radius is the same for the closest handle.
1747                                          _("Base radius to tip radius ratio"),
1748                                          "tools.shapes.star", "proportion", 0.5,
1749                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1750                                          0.01, 1.0, 0.01, 0.1,
1751                                          labels, values, G_N_ELEMENTS(labels),
1752                                          sp_stb_proportion_value_changed );
1753         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1754         g_object_set_data( holder, "prop_action", eact );
1755         }
1757         if ( !isFlatSided ) {
1758             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1759         } else {
1760             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1761         }
1763         /* Roundedness */
1764         {
1765         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1766         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1767         eact = create_adjustment_action( "RoundednessAction",
1768                                          _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1769                                          "tools.shapes.star", "rounded", 0.0,
1770                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1771                                          -10.0, 10.0, 0.01, 0.1,
1772                                          labels, values, G_N_ELEMENTS(labels),
1773                                          sp_stb_rounded_value_changed );
1774         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1775         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1776         }
1778         /* Randomization */
1779         {
1780         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1781         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1782         eact = create_adjustment_action( "RandomizationAction",
1783                                          _("Randomized:"), _("Scatter randomly the corners and angles"),
1784                                          "tools.shapes.star", "randomized", 0.0,
1785                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1786                                          -10.0, 10.0, 0.001, 0.01,
1787                                          labels, values, G_N_ELEMENTS(labels),
1788                                          sp_stb_randomized_value_changed, 0.1, 3 );
1789         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1790         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1791         }
1792     }
1794     {
1795         /* Reset */
1796         {
1797             GtkAction* act = gtk_action_new( "StarResetAction",
1798                                              _("Defaults"),
1799                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1800                                              GTK_STOCK_CLEAR );
1801             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1802             gtk_action_group_add_action( mainActions, act );
1803             gtk_action_set_sensitive( act, TRUE );
1804         }
1805     }
1807     sigc::connection *connection = new sigc::connection(
1808         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1809         );
1810     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1811     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1815 //########################
1816 //##       Rect         ##
1817 //########################
1819 static void sp_rtb_sensitivize( GObject *tbl )
1821     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1822     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1823     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1825     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1826         gtk_action_set_sensitive( not_rounded, FALSE );
1827     } else {
1828         gtk_action_set_sensitive( not_rounded, TRUE );
1829     }
1833 static void
1834 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1835                           void (*setter)(SPRect *, gdouble))
1837     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1839     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1840     SPUnit const *unit = tracker->getActiveUnit();
1842     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1843         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1844     }
1846     // quit if run by the attr_changed listener
1847     if (g_object_get_data( tbl, "freeze" )) {
1848         return;
1849     }
1851     // in turn, prevent listener from responding
1852     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1854     bool modmade = false;
1855     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1856     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1857         if (SP_IS_RECT(items->data)) {
1858             if (adj->value != 0) {
1859                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
1860             } else {
1861                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
1862             }
1863             modmade = true;
1864         }
1865     }
1867     sp_rtb_sensitivize( tbl );
1869     if (modmade) {
1870         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
1871                                    _("Change rectangle"));
1872     }
1874     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1877 static void
1878 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
1880     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
1883 static void
1884 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
1886     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
1889 static void
1890 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
1892     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
1895 static void
1896 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
1898     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
1903 static void
1904 sp_rtb_defaults( GtkWidget *widget, GObject *obj)
1906     GtkAdjustment *adj = 0;
1908     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
1909     gtk_adjustment_set_value(adj, 0.0);
1910     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
1911     gtk_adjustment_value_changed(adj);
1913     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
1914     gtk_adjustment_set_value(adj, 0.0);
1915     gtk_adjustment_value_changed(adj);
1917     sp_rtb_sensitivize( obj );
1920 static void rect_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1921                                        gchar const *old_value, gchar const *new_value,
1922                                        bool is_interactive, gpointer data)
1924     GObject *tbl = G_OBJECT(data);
1926     // quit if run by the _changed callbacks
1927     if (g_object_get_data( tbl, "freeze" )) {
1928         return;
1929     }
1931     // in turn, prevent callbacks from responding
1932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
1934     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1935     SPUnit const *unit = tracker->getActiveUnit();
1937     gpointer item = g_object_get_data( tbl, "item" );
1938     if (item && SP_IS_RECT(item)) {
1939         {
1940             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
1941             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
1942             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
1943         }
1945         {
1946             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
1947             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
1948             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
1949         }
1951         {
1952             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
1953             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
1954             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
1955         }
1957         {
1958             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
1959             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
1960             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
1961         }
1962     }
1964     sp_rtb_sensitivize( tbl );
1966     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1970 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
1971     NULL, /* child_added */
1972     NULL, /* child_removed */
1973     rect_tb_event_attr_changed,
1974     NULL, /* content_changed */
1975     NULL  /* order_changed */
1976 };
1978 /**
1979  *  \param selection should not be NULL.
1980  */
1981 static void
1982 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1984     int n_selected = 0;
1985     Inkscape::XML::Node *repr = NULL;
1986     SPItem *item = NULL;
1988     if ( g_object_get_data( tbl, "repr" ) ) {
1989         g_object_set_data( tbl, "item", NULL );
1990     }
1991     purge_repr_listener( tbl, tbl );
1993     for (GSList const *items = selection->itemList();
1994          items != NULL;
1995          items = items->next) {
1996         if (SP_IS_RECT((SPItem *) items->data)) {
1997             n_selected++;
1998             item = (SPItem *) items->data;
1999             repr = SP_OBJECT_REPR(item);
2000         }
2001     }
2003     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2005     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2007     if (n_selected == 0) {
2008         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2010         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2011         gtk_action_set_sensitive(w, FALSE);
2012         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2013         gtk_action_set_sensitive(h, FALSE);
2015     } else if (n_selected == 1) {
2016         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2017         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2019         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2020         gtk_action_set_sensitive(w, TRUE);
2021         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2022         gtk_action_set_sensitive(h, TRUE);
2024         if (repr) {
2025             g_object_set_data( tbl, "repr", repr );
2026             g_object_set_data( tbl, "item", item );
2027             Inkscape::GC::anchor(repr);
2028             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2029             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2030         }
2031     } else {
2032         // FIXME: implement averaging of all parameters for multiple selected
2033         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2034         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2035         sp_rtb_sensitivize( tbl );
2036     }
2040 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2042     EgeAdjustmentAction* eact = 0;
2044     {
2045         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2046         ege_output_action_set_use_markup( act, TRUE );
2047         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2048         g_object_set_data( holder, "mode_action", act );
2049     }
2051     // rx/ry units menu: create
2052     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2053     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2054     // fixme: add % meaning per cent of the width/height
2055     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2056     g_object_set_data( holder, "tracker", tracker );
2058     /* W */
2059     {
2060         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2061         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2062         eact = create_adjustment_action( "RectWidthAction",
2063                                          _("W:"), _("Width of rectangle"),
2064                                          "tools.shapes.rect", "width", 0,
2065                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2066                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2067                                          labels, values, G_N_ELEMENTS(labels),
2068                                          sp_rtb_width_value_changed );
2069         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2070         g_object_set_data( holder, "width_action", eact );
2071         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2072         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2073     }
2075     /* H */
2076     {
2077         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2078         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2079         eact = create_adjustment_action( "RectHeightAction",
2080                                          _("H:"), _("Height of rectangle"),
2081                                          "tools.shapes.rect", "height", 0,
2082                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2083                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2084                                          labels, values, G_N_ELEMENTS(labels),
2085                                          sp_rtb_height_value_changed );
2086         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2087         g_object_set_data( holder, "height_action", eact );
2088         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2089         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2090     }
2092     /* rx */
2093     {
2094         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2095         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2096         eact = create_adjustment_action( "RadiusXAction",
2097                                          _("Rx:"), _("Horizontal radius of rounded corners"),
2098                                          "tools.shapes.rect", "rx", 0,
2099                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2100                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2101                                          labels, values, G_N_ELEMENTS(labels),
2102                                          sp_rtb_rx_value_changed);
2103         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2104         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2105     }
2107     /* ry */
2108     {
2109         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2110         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2111         eact = create_adjustment_action( "RadiusYAction",
2112                                          _("Ry:"), _("Vertical radius of rounded corners"),
2113                                          "tools.shapes.rect", "ry", 0,
2114                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2115                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2116                                          labels, values, G_N_ELEMENTS(labels),
2117                                          sp_rtb_ry_value_changed);
2118         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2119         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2120     }
2122     // add the units menu
2123     {
2124         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), _("") );
2125         gtk_action_group_add_action( mainActions, act );
2126     }
2128     /* Reset */
2129     {
2130         InkAction* inky = ink_action_new( "RectResetAction",
2131                                           _("Not rounded"),
2132                                           _("Make corners sharp"),
2133                                           "squared_corner",
2134                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2135         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2136         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2137         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2138         g_object_set_data( holder, "not_rounded", inky );
2139     }
2141     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2142     sp_rtb_sensitivize( holder );
2144     sigc::connection *connection = new sigc::connection(
2145         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2146         );
2147     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2148     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2151 //########################
2152 //##       3D Box       ##
2153 //########################
2155 static void sp_3dbox_toggle_vp_changed (GtkToggleAction *act, GObject *dataKludge, Box3D::Axis axis)
2157     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2158     SPDocument *document = sp_desktop_document (desktop);
2159     Box3D::Perspective3D *persp = document->current_perspective;
2161     g_return_if_fail (is_single_axis_direction (axis));
2162     g_return_if_fail (persp);
2164     persp->toggle_boxes (axis);
2166     gchar *str;    
2167     switch (axis) {
2168         case Box3D::X:
2169             str = g_strdup ("box3d_angle_x_action");
2170             break;
2171         case Box3D::Y:
2172             str = g_strdup ("box3d_angle_y_action");
2173             break;
2174         case Box3D::Z:
2175             str = g_strdup ("box3d_angle_z_action");
2176             break;
2177         default:
2178             return;
2179     }
2180     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2181     if (angle_action) {
2182         gtk_action_set_sensitive (angle_action, !persp->get_vanishing_point (axis)->is_finite() );
2183     }
2185     // FIXME: Given how it is realized in the other tools, this is probably not the right way to do it,
2186     //        but without the if construct, we get continuous segfaults. Needs further investigation.
2187     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2188         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
2189                          _("3D Box: Change perspective"));
2190     }
2193 static void sp_3dbox_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2195     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::X);
2198 static void sp_3dbox_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2200     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Y);
2203 static void sp_3dbox_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2205     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Z);
2208 static void sp_3dbox_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Box3D::Axis axis )
2210     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2211     Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2213     if (persp) {
2214         double angle = adj->value * M_PI/180;
2215         persp->set_infinite_direction (axis, NR::Point (cos (angle), sin (angle)));
2217         // FIXME: See comment above; without the if construct we get segfaults during undo.
2218         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2219             sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2220                              _("3D Box: Change perspective"));
2221         }
2222     }
2223     //g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
2226 static void sp_3dbox_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2228     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::X);
2231 static void sp_3dbox_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2233     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Y);
2236 static void sp_3dbox_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2238     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Z);
2241 // normalize angle so that it lies in the interval [0,360]
2242 static double sp_3dbox_normalize_angle (double a) {
2243     double angle = a + ((int) (a/360.0))*360;
2244     if (angle < 0) {
2245         angle += 360.0;
2246     }
2247     return angle;
2250 static void sp_3dbox_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     GtkWidget *tbl = GTK_WIDGET(data);
2256     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2257     /***
2258     // quit if run by the _changed callbacks
2259     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2260         return;
2261     }
2263     // in turn, prevent callbacks from responding
2264     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2265     ***/
2267     if (!strcmp(name, "inkscape:perspective")) {
2268         GtkAdjustment *adj = 0;
2269         double angle;
2270         SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2271         Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2273         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2274         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::X)->get_angle());
2275         gtk_adjustment_set_value(adj, angle);
2277         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2278         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Y)->get_angle());
2279         gtk_adjustment_set_value(adj, angle);
2281         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2282         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Z)->get_angle());
2283         gtk_adjustment_set_value(adj, angle);
2284     }
2287 static Inkscape::XML::NodeEventVector sp_3dbox_tb_repr_events =
2289     NULL, /* child_added */
2290     NULL, /* child_removed */
2291     sp_3dbox_tb_event_attr_changed,
2292     NULL, /* content_changed */
2293     NULL  /* order_changed */
2294 };
2296 /**
2297  *  \param selection Should not be NULL.
2298  */
2299 static void
2300 sp_3dbox_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2302     Inkscape::XML::Node *repr = NULL;
2303     purge_repr_listener(tbl, tbl);
2305     SPItem *item = selection->singleItem();
2306     if (item) {
2307         repr = SP_OBJECT_REPR(item);
2308         if (repr) {
2309             g_object_set_data(tbl, "repr", repr);
2310             Inkscape::GC::anchor(repr);
2311             sp_repr_add_listener(repr, &sp_3dbox_tb_repr_events, tbl);
2312             sp_repr_synthesize_events(repr, &sp_3dbox_tb_repr_events, tbl);
2313         }
2314     }
2317 static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2319     EgeAdjustmentAction* eact = 0;
2320     SPDocument *document = sp_desktop_document (desktop);
2321     Box3D::Perspective3D *persp = document->current_perspective;
2322     bool toggled = false;
2324     /* angle of VP in X direction */
2325     eact = create_adjustment_action("3DBoxPosAngleXAction",
2326                                     _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2327                                     "tools.shapes.3dbox", "dir_vp_x", persp->get_vanishing_point (Box3D::X)->get_angle(),
2328                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2329                                     0.0, 360.0, 1.0, 10.0,
2330                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2331                                     sp_3dbox_vpx_angle_changed,
2332                                     0.1, 1);
2333     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2334     g_object_set_data(holder, "box3d_angle_x_action", eact);
2335     if (!persp->get_vanishing_point (Box3D::X)->is_finite()) {
2336         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2337     } else {
2338         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2339     }
2341     /* toggle VP in X direction */
2342     {
2343     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2344                                                   _("Toggle VP in X direction"),
2345                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2346                                                   "toggle_vp_x",
2347                                                   Inkscape::ICON_SIZE_DECORATION);
2348     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2349     if (persp) {
2350         toggled = !persp->get_vanishing_point(Box3D::X)->is_finite();
2351     }
2352     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2353     /* we connect the signal after setting the state to avoid switching the state again */
2354     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_x_changed), holder);
2355     }
2357     /* angle of VP in Y direction */
2358     eact = create_adjustment_action("3DBoxPosAngleYAction",
2359                                     _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2360                                     "tools.shapes.3dbox", "dir_vp_y", persp->get_vanishing_point (Box3D::Y)->get_angle(),
2361                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2362                                     0.0, 360.0, 1.0, 10.0,
2363                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2364                                     sp_3dbox_vpy_angle_changed,
2365                                     0.1, 1);
2366     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2367     g_object_set_data(holder, "box3d_angle_y_action", eact);
2368     if (!persp->get_vanishing_point (Box3D::Y)->is_finite()) {
2369         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2370     } else {
2371         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2372     }
2374     /* toggle VP in Y direction */
2375     {
2376     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2377                                                  _("Toggle VP in Y direction"),
2378                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2379                                                  "toggle_vp_y",
2380                                                  Inkscape::ICON_SIZE_DECORATION);
2381     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2382     if (persp) {
2383         toggled = !persp->get_vanishing_point(Box3D::Y)->is_finite();
2384     }
2385     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2386     /* we connect the signal after setting the state to avoid switching the state again */
2387     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_y_changed), holder);
2388     }
2390     /* angle of VP in Z direction */
2391     eact = create_adjustment_action("3DBoxPosAngleZAction",
2392                                     _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2393                                     "tools.shapes.3dbox", "dir_vp_z", persp->get_vanishing_point (Box3D::Z)->get_angle(),
2394                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2395                                      0.0, 360.0, 1.0, 10.0,
2396                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2397                                     sp_3dbox_vpz_angle_changed,
2398                                     0.1, 1);
2399                                     
2400     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2401     g_object_set_data(holder, "box3d_angle_z_action", eact);
2402     if (!persp->get_vanishing_point (Box3D::Z)->is_finite()) {
2403         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2404     } else {
2405         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2406     }
2408     /* toggle VP in Z direction */
2409     {
2410     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2411                                                  _("Toggle VP in Z direction"),
2412                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2413                                                  "toggle_vp_z",
2414                                                  Inkscape::ICON_SIZE_DECORATION);
2415     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2416     if (persp) {
2417         toggled = !persp->get_vanishing_point(Box3D::Z)->is_finite();
2418     }
2419     /* we connect the signal after setting the state to avoid switching the state again */
2420     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2421     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_z_changed), holder);
2422     }
2424     sigc::connection *connection = new sigc::connection(
2425         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_3dbox_toolbox_selection_changed), (GObject *)holder))
2426        );
2427     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2428     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2431 //########################
2432 //##       Spiral       ##
2433 //########################
2435 static void
2436 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2438     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2440     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2441         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2442     }
2444     // quit if run by the attr_changed listener
2445     if (g_object_get_data( tbl, "freeze" )) {
2446         return;
2447     }
2449     // in turn, prevent listener from responding
2450     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2452     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2454     bool modmade = false;
2455     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2456          items != NULL;
2457          items = items->next)
2458     {
2459         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2460             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2461             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2462             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2463             modmade = true;
2464         }
2465     }
2467     g_free(namespaced_name);
2469     if (modmade) {
2470         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2471                                    _("Change spiral"));
2472     }
2474     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2477 static void
2478 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2480     sp_spl_tb_value_changed(adj, tbl, "revolution");
2483 static void
2484 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2486     sp_spl_tb_value_changed(adj, tbl, "expansion");
2489 static void
2490 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2492     sp_spl_tb_value_changed(adj, tbl, "t0");
2495 static void
2496 sp_spl_tb_defaults(GtkWidget *widget, GtkObject *obj)
2498     GtkWidget *tbl = GTK_WIDGET(obj);
2500     GtkAdjustment *adj;
2502     // fixme: make settable
2503     gdouble rev = 5;
2504     gdouble exp = 1.0;
2505     gdouble t0 = 0.0;
2507     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2508     gtk_adjustment_set_value(adj, rev);
2509     gtk_adjustment_value_changed(adj);
2511     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2512     gtk_adjustment_set_value(adj, exp);
2513     gtk_adjustment_value_changed(adj);
2515     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2516     gtk_adjustment_set_value(adj, t0);
2517     gtk_adjustment_value_changed(adj);
2519     spinbutton_defocus(GTK_OBJECT(tbl));
2523 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2524                                          gchar const *old_value, gchar const *new_value,
2525                                          bool is_interactive, gpointer data)
2527     GtkWidget *tbl = GTK_WIDGET(data);
2529     // quit if run by the _changed callbacks
2530     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2531         return;
2532     }
2534     // in turn, prevent callbacks from responding
2535     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2537     GtkAdjustment *adj;
2538     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2539     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2541     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2542     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2544     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2545     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2547     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2551 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2552     NULL, /* child_added */
2553     NULL, /* child_removed */
2554     spiral_tb_event_attr_changed,
2555     NULL, /* content_changed */
2556     NULL  /* order_changed */
2557 };
2559 static void
2560 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2562     int n_selected = 0;
2563     Inkscape::XML::Node *repr = NULL;
2565     purge_repr_listener( tbl, tbl );
2567     for (GSList const *items = selection->itemList();
2568          items != NULL;
2569          items = items->next)
2570     {
2571         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2572             n_selected++;
2573             repr = SP_OBJECT_REPR((SPItem *) items->data);
2574         }
2575     }
2577     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2579     if (n_selected == 0) {
2580         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2581     } else if (n_selected == 1) {
2582         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2584         if (repr) {
2585             g_object_set_data( tbl, "repr", repr );
2586             Inkscape::GC::anchor(repr);
2587             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2588             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2589         }
2590     } else {
2591         // FIXME: implement averaging of all parameters for multiple selected
2592         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2593         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2594     }
2598 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2600     EgeAdjustmentAction* eact = 0;
2602     {
2603         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2604         ege_output_action_set_use_markup( act, TRUE );
2605         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2606         g_object_set_data( holder, "mode_action", act );
2607     }
2609     /* Revolution */
2610     {
2611         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2612         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2613         eact = create_adjustment_action( "SpiralRevolutionAction",
2614                                          _("Turns:"), _("Number of revolutions"),
2615                                          "tools.shapes.spiral", "revolution", 3.0,
2616                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2617                                          0.01, 1024.0, 0.1, 1.0,
2618                                          labels, values, G_N_ELEMENTS(labels),
2619                                          sp_spl_tb_revolution_value_changed, 1, 2);
2620         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2621     }
2623     /* Expansion */
2624     {
2625         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2626         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2627         eact = create_adjustment_action( "SpiralExpansionAction",
2628                                          _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2629                                          "tools.shapes.spiral", "expansion", 1.0,
2630                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2631                                          0.0, 1000.0, 0.01, 1.0,
2632                                          labels, values, G_N_ELEMENTS(labels),
2633                                          sp_spl_tb_expansion_value_changed);
2634         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2635     }
2637     /* T0 */
2638     {
2639         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2640         gdouble values[] = {0, 0.5, 0.9};
2641         eact = create_adjustment_action( "SpiralT0Action",
2642                                          _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2643                                          "tools.shapes.spiral", "t0", 0.0,
2644                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2645                                          0.0, 0.999, 0.01, 1.0,
2646                                          labels, values, G_N_ELEMENTS(labels),
2647                                          sp_spl_tb_t0_value_changed);
2648         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2649     }
2651     /* Reset */
2652     {
2653         InkAction* inky = ink_action_new( "SpiralResetAction",
2654                                           _("Defaults"),
2655                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2656                                           GTK_STOCK_CLEAR,
2657                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2658         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2659         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2660     }
2663     sigc::connection *connection = new sigc::connection(
2664         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2665         );
2666     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2667     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2670 //########################
2671 //##     Pen/Pencil    ##
2672 //########################
2675 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2677     // Put stuff here
2680 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2682     // Put stuff here
2685 //########################
2686 //##       Tweak        ##
2687 //########################
2689 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2691     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2694 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject *tbl )
2696     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2699 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer data )
2701     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2704 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2706     int mode = ege_select_one_action_get_active( act );
2707     prefs_set_int_attribute("tools.tweak", "mode", mode);
2709     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2710     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2711     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2712     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2713     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2714     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2715         if (doh) gtk_action_set_sensitive (doh, TRUE);
2716         if (dos) gtk_action_set_sensitive (dos, TRUE);
2717         if (dol) gtk_action_set_sensitive (dol, TRUE);
2718         if (doo) gtk_action_set_sensitive (doo, TRUE);
2719         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2720     } else {
2721         if (doh) gtk_action_set_sensitive (doh, FALSE);
2722         if (dos) gtk_action_set_sensitive (dos, FALSE);
2723         if (dol) gtk_action_set_sensitive (dol, FALSE);
2724         if (doo) gtk_action_set_sensitive (doo, FALSE);
2725         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2726     }
2729 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject *tbl )
2731     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2734 static void tweak_toggle_doh (GtkToggleAction *act, gpointer data) {
2735     bool show = gtk_toggle_action_get_active( act );
2736     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2738 static void tweak_toggle_dos (GtkToggleAction *act, gpointer data) {
2739     bool show = gtk_toggle_action_get_active( act );
2740     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2742 static void tweak_toggle_dol (GtkToggleAction *act, gpointer data) {
2743     bool show = gtk_toggle_action_get_active( act );
2744     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2746 static void tweak_toggle_doo (GtkToggleAction *act, gpointer data) {
2747     bool show = gtk_toggle_action_get_active( act );
2748     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
2751 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2753     {
2754         /* Width */
2755         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2756         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2757         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2758                                                               _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2759                                                               "tools.tweak", "width", 15,
2760                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2761                                                               1, 100, 1.0, 10.0,
2762                                                               labels, values, G_N_ELEMENTS(labels),
2763                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2764         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2765         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2766     }
2769     {
2770         /* Force */
2771         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2772         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2773         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2774                                                               _("Force:"), _("The force of the tweak action"),
2775                                                               "tools.tweak", "force", 20,
2776                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2777                                                               1, 100, 1.0, 10.0,
2778                                                               labels, values, G_N_ELEMENTS(labels),
2779                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2780         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2781         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2782     }
2784     {
2785         EgeOutputAction* act = ege_output_action_new( "TweakModeLabel", _("<b>Mode:</b>"), "", 0 );
2786         ege_output_action_set_use_markup( act, TRUE );
2787         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2788     }
2790     /* Mode */
2791     {
2792         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2794         GtkTreeIter iter;
2795         gtk_list_store_append( model, &iter );
2796         gtk_list_store_set( model, &iter,
2797                             0, _("Push mode"),
2798                             1, _("Push parts of paths in any direction"),
2799                             2, "tweak_push_mode",
2800                             -1 );
2802         gtk_list_store_append( model, &iter );
2803         gtk_list_store_set( model, &iter,
2804                             0, _("Shrink mode"),
2805                             1, _("Shrink (inset) parts of paths"),
2806                             2, "tweak_shrink_mode",
2807                             -1 );
2809         gtk_list_store_append( model, &iter );
2810         gtk_list_store_set( model, &iter,
2811                             0, _("Grow mode"),
2812                             1, _("Grow (outset) parts of paths"),
2813                             2, "tweak_grow_mode",
2814                             -1 );
2816         gtk_list_store_append( model, &iter );
2817         gtk_list_store_set( model, &iter,
2818                             0, _("Attract mode"),
2819                             1, _("Attract parts of paths towards cursor"),
2820                             2, "tweak_attract_mode",
2821                             -1 );
2823         gtk_list_store_append( model, &iter );
2824         gtk_list_store_set( model, &iter,
2825                             0, _("Repel mode"),
2826                             1, _("Repel parts of paths from cursor"),
2827                             2, "tweak_repel_mode",
2828                             -1 );
2830         gtk_list_store_append( model, &iter );
2831         gtk_list_store_set( model, &iter,
2832                             0, _("Roughen mode"),
2833                             1, _("Roughen parts of paths"),
2834                             2, "tweak_roughen_mode",
2835                             -1 );
2837         gtk_list_store_append( model, &iter );
2838         gtk_list_store_set( model, &iter,
2839                             0, _("Color paint mode"),
2840                             1, _("Paint the tool's color upon selected objects"),
2841                             2, "tweak_colorpaint_mode",
2842                             -1 );
2844         gtk_list_store_append( model, &iter );
2845         gtk_list_store_set( model, &iter,
2846                             0, _("Color jitter mode"),
2847                             1, _("Jitter the colors of selected objects"),
2848                             2, "tweak_colorjitter_mode",
2849                             -1 );
2851         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
2852         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2853         g_object_set_data( holder, "mode_action", act );
2855         ege_select_one_action_set_appearance( act, "full" );
2856         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2857         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2858         ege_select_one_action_set_icon_column( act, 2 );
2859         ege_select_one_action_set_tooltip_column( act, 1  );
2861         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2862         ege_select_one_action_set_active( act, mode );
2863         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
2865         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
2866     }
2868     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2870     {
2871         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
2872         ege_output_action_set_use_markup( act, TRUE );
2873         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2874         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2875             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2876         g_object_set_data( holder, "tweak_channels_label", act);
2877     }
2879     {
2880         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
2881                                                       _("H"),
2882                                                       _("In color mode, act on objects' hue"),
2883                                                       NULL,
2884                                                       Inkscape::ICON_SIZE_DECORATION );
2885         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2886         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
2887         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
2888         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2889             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2890         g_object_set_data( holder, "tweak_doh", act);
2891     }
2892     {
2893         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
2894                                                       _("S"),
2895                                                       _("In color mode, act on objects' saturation"),
2896                                                       NULL,
2897                                                       Inkscape::ICON_SIZE_DECORATION );
2898         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2899         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
2900         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
2901         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2902             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2903         g_object_set_data( holder, "tweak_dos", act );
2904     }
2905     {
2906         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
2907                                                       _("L"),
2908                                                       _("In color mode, act on objects' lightness"),
2909                                                       NULL,
2910                                                       Inkscape::ICON_SIZE_DECORATION );
2911         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2912         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
2913         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
2914         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2915             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2916         g_object_set_data( holder, "tweak_dol", act );
2917     }
2918     {
2919         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
2920                                                       _("O"),
2921                                                       _("In color mode, act on objects' opacity"),
2922                                                       NULL,
2923                                                       Inkscape::ICON_SIZE_DECORATION );
2924         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2925         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
2926         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
2927         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2928             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2929         g_object_set_data( holder, "tweak_doo", act );
2930     }
2932     {   /* Fidelity */
2933         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
2934         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
2935         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
2936                                                               _("Fidelity:"), _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
2937                                                               "tools.tweak", "fidelity", 50,
2938                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
2939                                                               1, 100, 1.0, 10.0,
2940                                                               labels, values, G_N_ELEMENTS(labels),
2941                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
2942         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2943         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2944     }
2947     /* Use Pressure button */
2948     {
2949         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
2950                                                       _("Pressure"),
2951                                                       _("Use the pressure of the input device to alter the force of tweak action"),
2952                                                       "use_pressure",
2953                                                       Inkscape::ICON_SIZE_DECORATION );
2954         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2955         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
2956         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
2957     }
2962 //########################
2963 //##     Calligraphy    ##
2964 //########################
2966 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
2968     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2971 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
2973     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2976 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
2978     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2981 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2983     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2986 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
2988     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
2991 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
2993     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
2996 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
2998     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3001 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3003     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3006 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer data )
3008     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3011 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer data )
3013     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3016 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3018     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3020     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3023 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3025     // FIXME: make defaults settable via Inkscape Options
3026     struct KeyValue {
3027         char const *key;
3028         double value;
3029     } const key_values[] = {
3030         {"mass", 0.02},
3031         {"wiggle", 0.0},
3032         {"angle", 30.0},
3033         {"width", 15},
3034         {"thinning", 0.1},
3035         {"tremor", 0.0},
3036         {"flatness", 0.9},
3037         {"cap_rounding", 0.0}
3038     };
3040     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3041         KeyValue const &kv = key_values[i];
3042         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3043         if ( adj ) {
3044             gtk_adjustment_set_value(adj, kv.value);
3045         }
3046     }
3050 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3052     {
3053         EgeAdjustmentAction* calligraphy_angle = 0;
3055         {
3056         /* Width */
3057         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3058         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3059         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3060                                          _("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
3061                                          "tools.calligraphic", "width", 15,
3062                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3063                                          1, 100, 1.0, 10.0,
3064                                          labels, values, G_N_ELEMENTS(labels),
3065                                          sp_ddc_width_value_changed,  0.01, 0, 100 );
3066         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3067         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3068         }
3070         {
3071         /* Thinning */
3072             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3073             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3074         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3075                                          _("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3076                                          "tools.calligraphic", "thinning", 0.1,
3077                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3078                                          -1.0, 1.0, 0.01, 0.1,
3079                                          labels, values, G_N_ELEMENTS(labels),
3080                                          sp_ddc_velthin_value_changed, 0.01, 2);
3081         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3082         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3083         }
3085         {
3086         /* Angle */
3087         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3088         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3089         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3090                                          _("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3091                                          "tools.calligraphic", "angle", 30,
3092                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3093                                          -90.0, 90.0, 1.0, 10.0,
3094                                          labels, values, G_N_ELEMENTS(labels),
3095                                          sp_ddc_angle_value_changed, 1, 0 );
3096         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3097         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3098         calligraphy_angle = eact;
3099         }
3101         {
3102         /* Fixation */
3103             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3104         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3105         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3106                                          _("Fixation:"), _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3107                                          "tools.calligraphic", "flatness", 0.9,
3108                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3109                                          0.0, 1.0, 0.01, 0.1,
3110                                          labels, values, G_N_ELEMENTS(labels),
3111                                          sp_ddc_flatness_value_changed, 0.01, 2 );
3112         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3113         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3114         }
3116         {
3117         /* Cap Rounding */
3118             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3119         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3120         // TRANSLATORS: "cap" means "end" (both start and finish) here
3121         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3122                                          _("Caps:"), _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3123                                          "tools.calligraphic", "cap_rounding", 0.0,
3124                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3125                                          0.0, 5.0, 0.01, 0.1,
3126                                          labels, values, G_N_ELEMENTS(labels),
3127                                          sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3128         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3129         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3130         }
3132         {
3133         /* Tremor */
3134             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3135         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3136         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3137                                          _("Tremor:"), _("Increase to make strokes rugged and trembling"),
3138                                          "tools.calligraphic", "tremor", 0.0,
3139                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3140                                          0.0, 1.0, 0.01, 0.1,
3141                                          labels, values, G_N_ELEMENTS(labels),
3142                                          sp_ddc_tremor_value_changed, 0.01, 2 );
3144         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3145         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3146         }
3148         {
3149         /* Wiggle */
3150         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3151         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3152         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3153                                          _("Wiggle:"), _("Increase to make the pen waver and wiggle"),
3154                                          "tools.calligraphic", "wiggle", 0.0,
3155                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3156                                          0.0, 1.0, 0.01, 0.1,
3157                                          labels, values, G_N_ELEMENTS(labels),
3158                                          sp_ddc_wiggle_value_changed, 0.01, 2 );
3159         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3160         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3161         }
3163         {
3164         /* Mass */
3165             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3166         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3167         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3168                                          _("Mass:"), _("Increase to make the pen drag behind, as if slowed by inertia"),
3169                                          "tools.calligraphic", "mass", 0.02,
3170                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3171                                          0.0, 1.0, 0.01, 0.1,
3172                                          labels, values, G_N_ELEMENTS(labels),
3173                                          sp_ddc_mass_value_changed, 0.01, 2 );
3174         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3175         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3176         }
3179         /* Trace Background button */
3180         {
3181             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3182                                                           _("Trace Background"),
3183                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3184                                                           "trace_background",
3185                                                           Inkscape::ICON_SIZE_DECORATION );
3186             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3187             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3188             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3189         }
3191         /* Use Pressure button */
3192         {
3193             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3194                                                           _("Pressure"),
3195                                                           _("Use the pressure of the input device to alter the width of the pen"),
3196                                                           "use_pressure",
3197                                                           Inkscape::ICON_SIZE_DECORATION );
3198             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3199             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3200             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3201         }
3203         /* Use Tilt button */
3204         {
3205             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3206                                                           _("Tilt"),
3207                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3208                                                           "use_tilt",
3209                                                           Inkscape::ICON_SIZE_DECORATION );
3210             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3211             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3212             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3213             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3214         }
3216         /* Reset */
3217         {
3218             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3219                                              _("Defaults"),
3220                                              _("Reset all parameters to defaults"),
3221                                              GTK_STOCK_CLEAR );
3222             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3223             gtk_action_group_add_action( mainActions, act );
3224             gtk_action_set_sensitive( act, TRUE );
3225         }
3226     }
3230 //########################
3231 //##    Circle / Arc    ##
3232 //########################
3234 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3236     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3237     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3239     if (v1 == 0 && v2 == 0) {
3240         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3241             gtk_action_set_sensitive( ocb, FALSE );
3242             gtk_action_set_sensitive( make_whole, FALSE );
3243         }
3244     } else {
3245         gtk_action_set_sensitive( ocb, TRUE );
3246         gtk_action_set_sensitive( make_whole, TRUE );
3247     }
3250 static void
3251 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3253     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3255     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3256         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3257     }
3259     // quit if run by the attr_changed listener
3260     if (g_object_get_data( tbl, "freeze" )) {
3261         return;
3262     }
3264     // in turn, prevent listener from responding
3265     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3267     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3269     bool modmade = false;
3270     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3271          items != NULL;
3272          items = items->next)
3273     {
3274         SPItem *item = SP_ITEM(items->data);
3276         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3278             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3279             SPArc *arc = SP_ARC(item);
3281             if (!strcmp(value_name, "start"))
3282                 ge->start = (adj->value * M_PI)/ 180;
3283             else
3284                 ge->end = (adj->value * M_PI)/ 180;
3286             sp_genericellipse_normalize(ge);
3287             ((SPObject *)arc)->updateRepr();
3288             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3290             modmade = true;
3291         }
3292     }
3294     g_free(namespaced_name);
3296     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3298     sp_arctb_sensitivize( tbl, adj->value, other->value );
3300     if (modmade) {
3301         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3302                                    _("Arc: Change start/end"));
3303     }
3305     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3309 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3311     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3314 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3316     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3319 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3321     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3322     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3323         if ( ege_select_one_action_get_active( act ) != 0 ) {
3324             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3325         } else {
3326             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3327         }
3328     }
3330     // quit if run by the attr_changed listener
3331     if (g_object_get_data( tbl, "freeze" )) {
3332         return;
3333     }
3335     // in turn, prevent listener from responding
3336     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3338     bool modmade = false;
3340     if ( ege_select_one_action_get_active(act) != 0 ) {
3341         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3342              items != NULL;
3343              items = items->next)
3344         {
3345             if (SP_IS_ARC((SPItem *) items->data)) {
3346                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3347                 repr->setAttribute("sodipodi:open", "true");
3348                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3349                 modmade = true;
3350             }
3351         }
3352     } else {
3353         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3354              items != NULL;
3355              items = items->next)
3356         {
3357             if (SP_IS_ARC((SPItem *) items->data))    {
3358                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3359                 repr->setAttribute("sodipodi:open", NULL);
3360                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3361                 modmade = true;
3362             }
3363         }
3364     }
3366     if (modmade) {
3367         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3368                                    _("Arc: Change open/closed"));
3369     }
3371     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3374 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3376     GtkAdjustment *adj;
3377     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3378     gtk_adjustment_set_value(adj, 0.0);
3379     gtk_adjustment_value_changed(adj);
3381     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3382     gtk_adjustment_set_value(adj, 0.0);
3383     gtk_adjustment_value_changed(adj);
3385     spinbutton_defocus( GTK_OBJECT(obj) );
3388 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
3389                                       gchar const *old_value, gchar const *new_value,
3390                                       bool is_interactive, gpointer data)
3392     GObject *tbl = G_OBJECT(data);
3394     // quit if run by the _changed callbacks
3395     if (g_object_get_data( tbl, "freeze" )) {
3396         return;
3397     }
3399     // in turn, prevent callbacks from responding
3400     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3402     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3403     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3405     GtkAdjustment *adj1,*adj2;
3406     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3407     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3408     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3409     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3411     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3413     char const *openstr = NULL;
3414     openstr = repr->attribute("sodipodi:open");
3415     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3417     if (openstr) {
3418         ege_select_one_action_set_active( ocb, 1 );
3419     } else {
3420         ege_select_one_action_set_active( ocb, 0 );
3421     }
3423     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3426 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3427     NULL, /* child_added */
3428     NULL, /* child_removed */
3429     arc_tb_event_attr_changed,
3430     NULL, /* content_changed */
3431     NULL  /* order_changed */
3432 };
3435 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3437     int n_selected = 0;
3438     Inkscape::XML::Node *repr = NULL;
3440     purge_repr_listener( tbl, tbl );
3442     for (GSList const *items = selection->itemList();
3443          items != NULL;
3444          items = items->next)
3445     {
3446         if (SP_IS_ARC((SPItem *) items->data)) {
3447             n_selected++;
3448             repr = SP_OBJECT_REPR((SPItem *) items->data);
3449         }
3450     }
3452     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3454     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3455     if (n_selected == 0) {
3456         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3457     } else if (n_selected == 1) {
3458         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3459         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3461         if (repr) {
3462             g_object_set_data( tbl, "repr", repr );
3463             Inkscape::GC::anchor(repr);
3464             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3465             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3466         }
3467     } else {
3468         // FIXME: implement averaging of all parameters for multiple selected
3469         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3470         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3471         sp_arctb_sensitivize( tbl, 1, 0 );
3472     }
3476 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3478     EgeAdjustmentAction* eact = 0;
3481     {
3482         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3483         ege_output_action_set_use_markup( act, TRUE );
3484         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3485         g_object_set_data( holder, "mode_action", act );
3486     }
3488     /* Start */
3489     {
3490         eact = create_adjustment_action( "ArcStartAction",
3491                                          _("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
3492                                          "tools.shapes.arc", "start", 0.0,
3493                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3494                                          -360.0, 360.0, 1.0, 10.0,
3495                                          0, 0, 0,
3496                                          sp_arctb_start_value_changed);
3497         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3498     }
3500     /* End */
3501     {
3502         eact = create_adjustment_action( "ArcEndAction",
3503                                          _("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
3504                                          "tools.shapes.arc", "end", 0.0,
3505                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3506                                          -360.0, 360.0, 1.0, 10.0,
3507                                          0, 0, 0,
3508                                          sp_arctb_end_value_changed);
3509         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3510     }
3512     /* Segments / Pie checkbox */
3513     {
3514         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3516         GtkTreeIter iter;
3517         gtk_list_store_append( model, &iter );
3518         gtk_list_store_set( model, &iter,
3519                             0, _("Closed arc"),
3520                             1, _("Switch to segment (closed shape with two radii)"),
3521                             2, "circle_closed_arc",
3522                             -1 );
3524         gtk_list_store_append( model, &iter );
3525         gtk_list_store_set( model, &iter,
3526                             0, _("Open Arc"),
3527                             1, _("Switch to arc (unclosed shape)"),
3528                             2, "circle_open_arc",
3529                             -1 );
3531         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
3532         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3533         g_object_set_data( holder, "open_action", act );
3535         ege_select_one_action_set_appearance( act, "full" );
3536         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3537         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3538         ege_select_one_action_set_icon_column( act, 2 );
3539         ege_select_one_action_set_tooltip_column( act, 1  );
3541         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3542         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3543         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3544         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3545     }
3547     /* Make Whole */
3548     {
3549         InkAction* inky = ink_action_new( "ArcResetAction",
3550                                           _("Make whole"),
3551                                           _("Make the shape a whole ellipse, not arc or segment"),
3552                                           "reset_circle",
3553                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3554         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3555         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3556         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3557         g_object_set_data( holder, "make_whole", inky );
3558     }
3560     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3561     // sensitivize make whole and open checkbox
3562     {
3563         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3564         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3565         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3566     }
3569     sigc::connection *connection = new sigc::connection(
3570         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3571         );
3572     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3573     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3579 // toggle button callbacks and updaters
3581 //########################
3582 //##      Dropper       ##
3583 //########################
3585 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3586     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3587     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3588     if ( set_action ) {
3589         if ( gtk_toggle_action_get_active( act ) ) {
3590             gtk_action_set_sensitive( set_action, TRUE );
3591         } else {
3592             gtk_action_set_sensitive( set_action, FALSE );
3593         }
3594     }
3596     spinbutton_defocus(GTK_OBJECT(tbl));
3599 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3600     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3601     spinbutton_defocus(GTK_OBJECT(tbl));
3605 /**
3606  * Dropper auxiliary toolbar construction and setup.
3607  *
3608  * TODO: Would like to add swatch of current color.
3609  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3610  *       can drag and drop places. Will provide a nice mixing palette.
3611  */
3612 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3614     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3616     {
3617         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3618                                                       _("Pick alpha"),
3619                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3620                                                       "color_alpha_get",
3621                                                       Inkscape::ICON_SIZE_DECORATION );
3622         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3623         g_object_set_data( holder, "pick_action", act );
3624         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3625         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3626     }
3628     {
3629         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3630                                                       _("Set alpha"),
3631                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3632                                                       "color_alpha_set",
3633                                                       Inkscape::ICON_SIZE_DECORATION );
3634         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3635         g_object_set_data( holder, "set_action", act );
3636         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3637         // make sure it's disabled if we're not picking alpha
3638         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3639         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3640     }
3644 //########################
3645 //##    Text Toolbox    ##
3646 //########################
3647 /*
3648 static void
3649 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3651     //Call back for letter sizing spinbutton
3654 static void
3655 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3657     //Call back for line height spinbutton
3660 static void
3661 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3663     //Call back for horizontal kerning spinbutton
3666 static void
3667 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3669     //Call back for vertical kerning spinbutton
3672 static void
3673 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3675     //Call back for letter rotation spinbutton
3676 }*/
3678 namespace {
3680 bool visible = false;
3682 void
3683 sp_text_toolbox_selection_changed (Inkscape::Selection *selection, GObject *tbl)
3685     SPStyle *query =
3686         sp_style_new (SP_ACTIVE_DOCUMENT);
3688     int result_family =
3689         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3691     int result_style =
3692         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3694     int result_numbers =
3695         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3697     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3699     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3700     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3701     {
3702         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3704         if (repr)
3705         {
3706             sp_style_read_from_repr (query, repr);
3707         }
3708         else
3709         {
3710             return;
3711         }
3712     }
3714     if (query->text)
3715     {
3716         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3717             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3718             gtk_entry_set_text (GTK_ENTRY (entry), "");
3720         } else if (query->text->font_family.value) {
3722             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3723             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3725             Gtk::TreePath path;
3726             try {
3727                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3728             } catch (...) {
3729                 return;
3730             }
3732             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3733             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3735             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3737             gtk_tree_selection_select_path (tselection, path.gobj());
3738             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3740             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3741         }
3743         //Size
3744         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3745         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3746         g_object_set_data (tbl, "size-block", gpointer(1));
3747         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3748         g_object_set_data (tbl, "size-block", gpointer(0));
3749         free (str);
3751         //Anchor
3752         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3753         {
3754             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3755             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3756             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3757             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3758         }
3759         else
3760         {
3761             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3762             {
3763                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3764                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3765                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3766                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3767             }
3768             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3769             {
3770                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3771                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3772                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3773                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3774             }
3775             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3776             {
3777                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3778                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3779                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3780                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3781             }
3782         }
3784         //Style
3785         {
3786             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3788             gboolean active = gtk_toggle_button_get_active (button);
3789             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3791             if (active != check)
3792             {
3793                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3794                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3795                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3796             }
3797         }
3799         {
3800             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3802             gboolean active = gtk_toggle_button_get_active (button);
3803             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3805             if (active != check)
3806             {
3807                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3808                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3809                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3810             }
3811         }
3813         //Orientation
3814         //locking both buttons, changing one affect all group (both)
3815         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3816         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3818         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3819         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3821         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3822         {
3823             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3824         }
3825         else
3826         {
3827             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3828         }
3829         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3830         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3831     }
3833     sp_style_unref(query);
3836 void
3837 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint flags, GObject *tbl)
3839     sp_text_toolbox_selection_changed (selection, tbl);
3842 void
3843 sp_text_toolbox_subselection_changed (gpointer dragger, GObject *tbl)
3845     sp_text_toolbox_selection_changed (NULL, tbl);
3848 void
3849 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3850                                 GObject             *tbl)
3852     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3853     GtkTreeModel *model = 0;
3854     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3855     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3856     GtkTreeIter   iter;
3857     char         *family = 0;
3859     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3860     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3862     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3863         return;
3864     }
3866     gtk_tree_model_get (model, &iter, 0, &family, -1);
3868     if (g_object_get_data (G_OBJECT (selection), "block"))
3869     {
3870         gtk_entry_set_text (GTK_ENTRY (entry), family);
3871         return;
3872     }
3874     gtk_widget_hide (popdown);
3875     visible = false;
3877     gtk_entry_set_text (GTK_ENTRY (entry), family);
3879     SPStyle *query =
3880         sp_style_new (SP_ACTIVE_DOCUMENT);
3882     int result_numbers =
3883         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3885     SPCSSAttr *css = sp_repr_css_attr_new ();
3886     sp_repr_css_set_property (css, "font-family", family);
3888     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3889     if (result_numbers == QUERY_STYLE_NOTHING)
3890     {
3891         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3892         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3893     }
3894     else
3895     {
3896         sp_desktop_set_style (desktop, css, true, true);
3897     }
3899     sp_style_unref(query);
3901     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3902                                    _("Text: Change font family"));
3903     sp_repr_css_attr_unref (css);
3904     free (family);
3905     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3907     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3910 void
3911 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3912                                        GObject      *tbl)
3914     const char *family = gtk_entry_get_text (entry);
3916     try {
3917         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3918         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3919         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3920         gtk_tree_selection_select_path (selection, path.gobj());
3921         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3922         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3923     } catch (...) {
3924         if (family && strlen (family))
3925         {
3926             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3927         }
3928     }
3931 void
3932 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3933                                    gpointer          data)
3935     if (g_object_get_data (G_OBJECT (button), "block")) return;
3936     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3937     int prop = GPOINTER_TO_INT(data);
3939     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3940     SPCSSAttr *css = sp_repr_css_attr_new ();
3942     switch (prop)
3943     {
3944         case 0:
3945         {
3946             sp_repr_css_set_property (css, "text-anchor", "start");
3947             sp_repr_css_set_property (css, "text-align", "start");
3948             break;
3949         }
3950         case 1:
3951         {
3952             sp_repr_css_set_property (css, "text-anchor", "middle");
3953             sp_repr_css_set_property (css, "text-align", "center");
3954             break;
3955         }
3957         case 2:
3958         {
3959             sp_repr_css_set_property (css, "text-anchor", "end");
3960             sp_repr_css_set_property (css, "text-align", "end");
3961             break;
3962         }
3964         case 3:
3965         {
3966             sp_repr_css_set_property (css, "text-anchor", "start");
3967             sp_repr_css_set_property (css, "text-align", "justify");
3968             break;
3969         }
3970     }
3972     SPStyle *query =
3973         sp_style_new (SP_ACTIVE_DOCUMENT);
3974     int result_numbers =
3975         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3977     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3978     if (result_numbers == QUERY_STYLE_NOTHING)
3979     {
3980         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3981     }
3983     sp_style_unref(query);
3985     sp_desktop_set_style (desktop, css, true, true);
3986     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3987                                    _("Text: Change alignment"));
3988     sp_repr_css_attr_unref (css);
3990     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3993 void
3994 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
3995                                gpointer          data)
3997     if (g_object_get_data (G_OBJECT (button), "block")) return;
3999     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4000     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4001     int          prop       = GPOINTER_TO_INT(data);
4002     bool         active     = gtk_toggle_button_get_active (button);
4005     switch (prop)
4006     {
4007         case 0:
4008         {
4009             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4010             break;
4011         }
4013         case 1:
4014         {
4015             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4016             break;
4017         }
4018     }
4020     SPStyle *query =
4021         sp_style_new (SP_ACTIVE_DOCUMENT);
4022     int result_numbers =
4023         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4025     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4026     if (result_numbers == QUERY_STYLE_NOTHING)
4027     {
4028         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4029     }
4031     sp_style_unref(query);
4033     sp_desktop_set_style (desktop, css, true, true);
4034     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4035                                    _("Text: Change font style"));
4036     sp_repr_css_attr_unref (css);
4038     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4041 void
4042 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4043                                      gpointer         data)
4045     if (g_object_get_data (G_OBJECT (button), "block")) {
4046         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4047         return;
4048     }
4050     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4051     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4052     int          prop       = GPOINTER_TO_INT(data);
4054     switch (prop)
4055     {
4056         case 0:
4057         {
4058             sp_repr_css_set_property (css, "writing-mode", "lr");
4059             break;
4060         }
4062         case 1:
4063         {
4064             sp_repr_css_set_property (css, "writing-mode", "tb");
4065             break;
4066         }
4067     }
4069     SPStyle *query =
4070         sp_style_new (SP_ACTIVE_DOCUMENT);
4071     int result_numbers =
4072         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4074     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4075     if (result_numbers == QUERY_STYLE_NOTHING)
4076     {
4077         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4078     }
4080     sp_desktop_set_style (desktop, css, true, true);
4081     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4082                                    _("Text: Change orientation"));
4083     sp_repr_css_attr_unref (css);
4085     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4088 gboolean
4089 sp_text_toolbox_size_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
4091     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4092     if (!desktop) return FALSE;
4094     switch (get_group0_keyval (event)) {
4095         case GDK_Escape: // defocus
4096             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4097             return TRUE; // I consumed the event
4098             break;
4099         case GDK_Return: // defocus
4100         case GDK_KP_Enter:
4101             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4102             return TRUE; // I consumed the event
4103             break;
4104     }
4105     return FALSE;
4108 gboolean
4109 sp_text_toolbox_family_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
4111     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4112     if (!desktop) return FALSE;
4114     switch (get_group0_keyval (event)) {
4115         case GDK_Escape: // defocus
4116             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4117             sp_text_toolbox_selection_changed (NULL, tbl); // update
4118             return TRUE; // I consumed the event
4119             break;
4120     }
4121     return FALSE;
4124 gboolean
4125 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
4127     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4128     if (!desktop) return FALSE;
4130     switch (get_group0_keyval (event)) {
4131         case GDK_Escape: // defocus
4132             gtk_widget_hide (w);
4133             visible = false;
4134             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4135             return TRUE; // I consumed the event
4136             break;
4137     }
4138     return FALSE;
4142 void
4143 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4144                                GObject     *tbl)
4146     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4148     if (g_object_get_data (tbl, "size-block")) return;
4150 #if GTK_CHECK_VERSION(2,6,0)
4151     char *text = gtk_combo_box_get_active_text (cbox);
4152 #else // GTK_CHECK_VERSION(2,6,0)
4153     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
4154     GtkTreeIter iter;
4155     char *text = NULL;
4157     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
4158         gtk_tree_model_get (model, &iter, 0, &text, -1);
4159 #endif // GTK_CHECK_VERSION(2,6,0)
4161     SPCSSAttr *css = sp_repr_css_attr_new ();
4162     sp_repr_css_set_property (css, "font-size", text);
4163     free (text);
4165     SPStyle *query =
4166         sp_style_new (SP_ACTIVE_DOCUMENT);
4167     int result_numbers =
4168         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4170     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4171     if (result_numbers == QUERY_STYLE_NOTHING)
4172     {
4173         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4174     }
4176     sp_style_unref(query);
4178     sp_desktop_set_style (desktop, css, true, true);
4179     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4180                                    _("Text: Change font size"));
4181     sp_repr_css_attr_unref (css);
4184     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4185         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4188 void
4189 sp_text_toolbox_text_popdown_clicked    (GtkButton          *button,
4190                                          GObject            *tbl)
4192     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4193     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4194     int x, y;
4196     if (!visible)
4197     {
4198         gdk_window_get_origin (widget->window, &x, &y);
4199         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4200         gtk_widget_show_all (popdown);
4202         gdk_pointer_grab (widget->window, TRUE,
4203                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4204                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4205                                         GDK_POINTER_MOTION_MASK),
4206                           NULL, NULL, GDK_CURRENT_TIME);
4208         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4210         visible = true;
4211     }
4212     else
4213     {
4214         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4215         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4216         gtk_widget_hide (popdown);
4217         visible = false;
4218     }
4221 gboolean
4222 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4223                                  GdkEventFocus    *event,
4224                                  GObject          *tbl)
4226     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4227     return FALSE;
4230 gboolean
4231 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4232                                    GdkEventFocus    *event,
4233                                    GObject          *tbl)
4235     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4237     gtk_widget_hide (popdown);
4238     visible = false;
4239     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4240     return TRUE;
4243 void
4244 cell_data_func  (GtkTreeViewColumn *column,
4245                  GtkCellRenderer   *cell,
4246                  GtkTreeModel      *tree_model,
4247                  GtkTreeIter       *iter,
4248                  gpointer           data)
4250     char        *family,
4251         *family_escaped,
4252         *sample_escaped;
4254     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4256     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4258     family_escaped = g_markup_escape_text (family, -1);
4259     sample_escaped = g_markup_escape_text (sample, -1);
4261     std::stringstream markup;
4262     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4263     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4265     free (family);
4266     free (family_escaped);
4267     free (sample_escaped);
4270 static void delete_completion(GObject *obj, GtkWidget *entry) {
4271     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4272     if (completion) {
4273         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4274         g_object_unref (completion);
4275     }
4278 GtkWidget*
4279 sp_text_toolbox_new (SPDesktop *desktop)
4281     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4283     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4284     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4286     GtkTooltips *tt = gtk_tooltips_new();
4287     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4289     ////////////Family
4290     //Window
4291     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4292     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4294     //Entry
4295     GtkWidget           *entry = gtk_entry_new ();
4296     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4297     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4298     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4299     gtk_entry_completion_set_text_column (completion, 0);
4300     gtk_entry_completion_set_minimum_key_length (completion, 1);
4301     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4302     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4303     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4304     aux_toolbox_space (tbl, 1);
4305     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4306     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4308     //Button
4309     GtkWidget   *button = gtk_button_new ();
4310     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4311     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4313     //Popdown
4314     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4315     GtkWidget           *treeview = gtk_tree_view_new ();
4317     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4318     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4319     gtk_tree_view_column_pack_start (column, cell, FALSE);
4320     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4321     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4322     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4324     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4325     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4326 #if GTK_CHECK_VERSION(2,6,0)
4327     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4328 #endif // GTK_CHECK_VERSION(2,6,0)
4330     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4332     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4333     gtk_container_add (GTK_CONTAINER (sw), treeview);
4335     gtk_container_add (GTK_CONTAINER (window), sw);
4336     gtk_widget_set_size_request (window, 300, 450);
4338     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4339     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4340     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4342     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4344     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4345     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4347     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4348     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4350     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4351     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4352     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4353     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4354     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4356     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4357     aux_toolbox_space (tbl, 1);
4358     GtkWidget *box = gtk_event_box_new ();
4359     gtk_container_add (GTK_CONTAINER (box), image);
4360     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4361     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4362     GtkTooltips *tooltips = gtk_tooltips_new ();
4363     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4364     gtk_widget_hide (GTK_WIDGET (box));
4365     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4367     ////////////Size
4368     const char *sizes[] = {
4369         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4370         "16", "18", "20", "22", "24", "28",
4371         "32", "36", "40", "48", "56", "64", "72", "144"
4372     };
4374     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4375     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4376     gtk_widget_set_size_request (cbox, 80, -1);
4377     aux_toolbox_space (tbl, 1);
4378     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4379     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4380     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4381     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4383     //spacer
4384     aux_toolbox_space (tbl, 4);
4385     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4387     ////////////Text anchor
4388     GtkWidget *group   = gtk_radio_button_new (NULL);
4389     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4390     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4392     // left
4393     GtkWidget *rbutton = group;
4394     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4395     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4396     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4398     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4399     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4400     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4401     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4403     // center
4404     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4405     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4406     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4407     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4409     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4410     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4411     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4412     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4414     // right
4415     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4416     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4417     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4418     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4420     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4421     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4422     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4423     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4425     // fill
4426     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4427     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4428     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4429     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4431     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4432     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4433     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4434     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4436     aux_toolbox_space (tbl, 1);
4437     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4439     //spacer
4440     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4442     ////////////Text style
4443     row = gtk_hbox_new (FALSE, 4);
4445     // bold
4446     rbutton = gtk_toggle_button_new ();
4447     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4448     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4449     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4450     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4452     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4453     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4454     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4456     // italic
4457     rbutton = gtk_toggle_button_new ();
4458     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4459     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4460     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4461     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4463     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4464     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4465     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4467     aux_toolbox_space (tbl, 1);
4468     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4470     //spacer
4471     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4473     ////////////Text orientation
4474     group   = gtk_radio_button_new (NULL);
4475     row     = gtk_hbox_new (FALSE, 4);
4476     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4478     // horizontal
4479     rbutton = group;
4480     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4481     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4482     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4483     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4485     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4486     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4487     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4489     // vertical
4490     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4491     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4492     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4493     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4494     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4496     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4497     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4498     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4499     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4502     //watch selection
4503     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4505     sigc::connection *c_selection_changed =
4506         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4507                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4508     pool->add_connection ("selection-changed", c_selection_changed);
4510     sigc::connection *c_selection_modified =
4511         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4512                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4513     pool->add_connection ("selection-modified", c_selection_modified);
4515     sigc::connection *c_subselection_changed =
4516         new sigc::connection (desktop->connectToolSubselectionChanged
4517                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4518     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4520     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4523 #if 0
4524     // horizontal
4525     {
4526         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4527         GtkWidget *b = group = gtk_radio_button_new (NULL);
4528         gtk_container_add (GTK_CONTAINER (b), px);
4529         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4530         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4531         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4532         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4533     }
4535     // vertical
4536     {
4537         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4538         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4539         gtk_container_add (GTK_CONTAINER (b), px);
4540         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4541         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4542         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4543         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4544     }
4546     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4548     // letter spacing
4549     {
4550         {
4551             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4552             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4553             gtk_container_add (GTK_CONTAINER (hb), image);
4554             gtk_widget_show(image);
4555             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4556         }
4558         {
4559             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between letters"),
4560                                              "tools.text", "letter_spacing", 0.0,
4561                                              us, tbl, FALSE, NULL,
4562                                              -1000.0, 1000.0, 0.1, 0.1,
4563                                              sp_text_letter_changed, 0.1, 1);
4564             gtk_widget_set_size_request (hb, 45, 6);
4565             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4566         }
4567     }
4569     // line spacing
4570     {
4571         {
4572             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4573             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4574             gtk_container_add (GTK_CONTAINER (hb), image);
4575             gtk_widget_show(image);
4576             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4577         }
4579         {
4580             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between lines"),
4581                                              "tools.text", "line_spacing", 0,
4582                                              us, tbl, FALSE, NULL,
4583                                              -1000.0, 1000.0, 0.1, 0.1,
4584                                              sp_text_line_changed, 0.1, 1);
4585             gtk_widget_set_size_request (hb, 45, 0);
4586             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4587         }
4588     }
4590     {
4591         // horizontal kerning/vertical kerning units menu: create
4592         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4593         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4594         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4596         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4598         // horizontal kerning
4599         {
4600             {
4601                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4602                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4603                 gtk_container_add (GTK_CONTAINER (hb), image);
4604                 gtk_widget_show(image);
4605                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4606             }
4608             {
4609                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Horizontal kerning"),
4610                                                  "tools.text", "horizontal_kerning", 0,
4611                                                  us, tbl, FALSE, NULL,
4612                                                  -100.00, 100.00, 0.01, 0.1,
4613                                                  sp_text_horiz_kern_changed);
4614                 gtk_widget_set_size_request (hb, 45, 0);
4615                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4616             }
4617         }
4619         // vertical kerning
4620         {
4621             {
4622                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4623                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4624                 gtk_container_add (GTK_CONTAINER (hb), image);
4625                 gtk_widget_show(image);
4626                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4627             }
4629             {
4630                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Vertical kerning"),
4631                                                  "tools.text", "vertical_kerning", 0,
4632                                                  us, tbl, FALSE, NULL,
4633                                                  -100.00, 100.00, 0.01, 0.1,
4634                                                  sp_text_vert_kern_changed);
4635                 gtk_widget_set_size_request (hb, 45, 0);
4636                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4637             }
4638         }
4640         // add the units menu
4641         gtk_widget_show(us);
4642         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4643         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4644     }
4646     // letter rotation
4647     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4648     {
4649         {
4650             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4651             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4652             gtk_container_add (GTK_CONTAINER (hb), image);
4653             gtk_widget_show(image);
4654             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4655         }
4656         {
4657             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Letter rotation"),
4658                                              "tools.text", "letter_rotation", 0,
4659                                              us, tbl, FALSE, NULL,
4660                                              -180.0, 180.0, 0.1, 0.1,
4661                                              sp_text_letter_rotation_changed, 0.1, 1);
4662             gtk_widget_set_size_request (hb, 45, 0);
4663             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4664         }
4665         // rotation degree label
4666         {
4667             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4668             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4669         }
4670     }
4672     // Remove Manual Kerns
4673     {
4674         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4675         GtkWidget *button = gtk_button_new ();
4676         gtk_container_add (GTK_CONTAINER (button), px);
4677         gtk_widget_show(button);
4678         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4679         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4680         gtk_widget_set_sensitive(button, TRUE);
4681         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4682     }
4683 #endif
4685     gtk_widget_show_all (tbl);
4686     return tbl;
4688 } // end of sp_text_toolbox_new()
4690 }//<unnamed> namespace
4693 //#########################
4694 //##  Connector Toolbox  ##
4695 //#########################
4697 static void sp_connector_path_set_avoid(void)
4699     cc_selection_set_avoid(true);
4703 static void sp_connector_path_set_ignore(void)
4705     cc_selection_set_avoid(false);
4710 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4712     // quit if run by the _changed callbacks
4713     if (g_object_get_data( tbl, "freeze" )) {
4714         return;
4715     }
4717     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4718     SPDocument *doc = sp_desktop_document(desktop);
4720     if (!sp_document_get_undo_sensitive(doc))
4721     {
4722         return;
4723     }
4725     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4727     if ( repr->attribute("inkscape:connector-spacing") ) {
4728         gdouble priorValue = gtk_adjustment_get_value(adj);
4729         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4730         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4731             return;
4732         }
4733     } else if ( adj->value == defaultConnSpacing ) {
4734         return;
4735     }
4737     // in turn, prevent callbacks from responding
4738     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4740     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4741     SP_OBJECT(desktop->namedview)->updateRepr();
4743     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4744     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4745         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4746         NR::Matrix m = NR::identity();
4747         avoid_item_move(&m, item);
4748     }
4750     if (items) {
4751         g_slist_free(items);
4752     }
4754     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4755             _("Change connector spacing"));
4757     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4759     spinbutton_defocus(GTK_OBJECT(tbl));
4762 static void sp_connector_graph_layout(void)
4764     if (!SP_ACTIVE_DESKTOP) return;
4766     // hack for clones, see comment in align-and-distribute.cpp
4767     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4768     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4770     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4772     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4774     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4777 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4779     if ( gtk_toggle_action_get_active( act ) ) {
4780         prefs_set_string_attribute("tools.connector", "directedlayout",
4781                 "true");
4782     } else {
4783         prefs_set_string_attribute("tools.connector", "directedlayout",
4784                 "false");
4785     }
4788 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4790     if ( gtk_toggle_action_get_active( act ) ) {
4791         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4792                 "true");
4793     } else {
4794         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4795                 "false");
4796     }
4800 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4802     prefs_set_double_attribute("tools.connector", "length", adj->value);
4803     spinbutton_defocus(GTK_OBJECT(tbl));
4806 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4807         gchar const *name, gchar const *old_value, gchar const *new_value,
4808         bool is_interactive, gpointer data)
4810     GtkWidget *tbl = GTK_WIDGET(data);
4812     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4813         return;
4814     }
4815     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4816         return;
4817     }
4819     GtkAdjustment *adj = (GtkAdjustment*)
4820             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4821     gdouble spacing = defaultConnSpacing;
4822     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4824     gtk_adjustment_set_value(adj, spacing);
4828 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4829     NULL, /* child_added */
4830     NULL, /* child_removed */
4831     connector_tb_event_attr_changed,
4832     NULL, /* content_changed */
4833     NULL  /* order_changed */
4834 };
4837 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4839     {
4840         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4841                                           _("Avoid"),
4842                                           _("Make connectors avoid selected objects"),
4843                                           "connector_avoid",
4844                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4845         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
4846         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4847     }
4849     {
4850         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
4851                                           _("Ignore"),
4852                                           _("Make connectors ignore selected objects"),
4853                                           "connector_ignore",
4854                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4855         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
4856         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4857     }
4859     EgeAdjustmentAction* eact = 0;
4861     // Spacing spinbox
4862     eact = create_adjustment_action( "ConnectorSpacingAction",
4863                                      _("Spacing:"), _("The amount of space left around objects by auto-routing connectors"),
4864                                      "tools.connector", "spacing", defaultConnSpacing,
4865                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
4866                                      0, 100, 1.0, 10.0,
4867                                      0, 0, 0,
4868                                      connector_spacing_changed, 1, 0 );
4869     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4871     // Graph (connector network) layout
4872     {
4873         InkAction* inky = ink_action_new( "ConnectorGraphAction",
4874                                           _("Graph"),
4875                                           _("Nicely arrange selected connector network"),
4876                                           "graph_layout",
4877                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4878         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
4879         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4880     }
4882     // Default connector length spinbox
4883     eact = create_adjustment_action( "ConnectorLengthAction",
4884                                      _("Length:"), _("Ideal length for connectors when layout is applied"),
4885                                      "tools.connector", "length", 100,
4886                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
4887                                      10, 1000, 10.0, 100.0,
4888                                      0, 0, 0,
4889                                      connector_length_changed, 1, 0 );
4890     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4893     // Directed edges toggle button
4894     {
4895         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
4896                                                       _("Downwards"),
4897                                                       _("Make connectors with end-markers (arrows) point downwards"),
4898                                                       "directed_graph",
4899                                                       Inkscape::ICON_SIZE_DECORATION );
4900         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4902         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
4903         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4904                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4906         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
4907     }
4909     // Avoid overlaps toggle button
4910     {
4911         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
4912                                                       _("Remove overlaps"),
4913                                                       _("Do not allow overlapping shapes"),
4914                                                       "remove_overlaps",
4915                                                       Inkscape::ICON_SIZE_DECORATION );
4916         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4918         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
4919         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4920                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4922         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
4923     }
4925     // Code to watch for changes to the connector-spacing attribute in
4926     // the XML.
4927     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4928     g_assert(repr != NULL);
4930     purge_repr_listener( holder, holder );
4932     if (repr) {
4933         g_object_set_data( holder, "repr", repr );
4934         Inkscape::GC::anchor(repr);
4935         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
4936         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
4937     }
4938 } // end of sp_connector_toolbox_prep()
4940 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* tbl)
4942     gint channels = ege_select_one_action_get_active( act );
4943     flood_channels_set_channels( channels );
4946 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject *tbl)
4948     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4951 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject *tbl)
4953     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
4956 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
4958     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
4959     SPUnit const *unit = tracker->getActiveUnit();
4962     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4965 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
4967     // FIXME: make defaults settable via Inkscape Options
4968     struct KeyValue {
4969         char const *key;
4970         double value;
4971     } const key_values[] = {
4972         {"threshold", 15},
4973         {"offset", 0.0}
4974     };
4976     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
4977         KeyValue const &kv = key_values[i];
4978         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
4979         if ( adj ) {
4980             gtk_adjustment_set_value(adj, kv.value);
4981         }
4982     }
4983     
4984     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
4985     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
4986     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
4987     ege_select_one_action_set_active( autogap_action, 0 );
4990 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4992     EgeAdjustmentAction* eact = 0;
4994     {
4995         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4997         GList* items = 0;
4998         gint count = 0;
4999         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5000         {
5001             GtkTreeIter iter;
5002             gtk_list_store_append( model, &iter );
5003             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5004             count++;
5005         }
5006         g_list_free( items );
5007         items = 0;
5008         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by:"), _(""), NULL, GTK_TREE_MODEL(model) );
5009         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5010         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5011         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5012         g_object_set_data( holder, "channels_action", act1 );
5013     }
5015     // Spacing spinbox
5016     {
5017         eact = create_adjustment_action(
5018             "ThresholdAction",
5019             _("Threshold:"),
5020             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5021             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5022             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5023             0, 0, 0,
5024             paintbucket_threshold_changed, 1, 0 );
5026         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5027     }
5029     // Create the units menu.
5030     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5031     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
5032     g_object_set_data( holder, "tracker", tracker );
5033     {
5034         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), _("") );
5035         gtk_action_group_add_action( mainActions, act );
5036     }
5038     // Offset spinbox
5039     {
5040         eact = create_adjustment_action(
5041             "OffsetAction",
5042             _("Grow/shrink by:"),
5043             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5044             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5045             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5046             0, 0, 0,
5047             paintbucket_offset_changed, 1, 2);
5048         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5050         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5051     }
5052     
5053     /* Auto Gap */
5054     {
5055         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5057         GList* items = 0;
5058         gint count = 0;
5059         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5060         {
5061             GtkTreeIter iter;
5062             gtk_list_store_append( model, &iter );
5063             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5064             count++;
5065         }
5066         g_list_free( items );
5067         items = 0;
5068         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Fill gaps:"), _(""), NULL, GTK_TREE_MODEL(model) );
5069         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5070         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5071         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5072         g_object_set_data( holder, "autogap_action", act2 );
5073     }
5075     /* Reset */
5076     {
5077         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5078                                           _("Defaults"),
5079                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5080                                           GTK_STOCK_CLEAR );
5081         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5082         gtk_action_group_add_action( mainActions, act );
5083         gtk_action_set_sensitive( act, TRUE );
5084     }
5088 /*
5089   Local Variables:
5090   mode:c++
5091   c-file-style:"stroustrup"
5092   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5093   indent-tabs-mode:nil
5094   fill-column:99
5095   End:
5096 */
5097 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :