Code

fd8c56c91d47785b1ee9a79d87c5f37c4caec74e
[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='TweakFidelityAction' />"
255         "    <separator />"
256         "    <toolitem action='TweakChannelsLabel' />"
257         "    <toolitem action='TweakDoH' />"
258         "    <toolitem action='TweakDoS' />"
259         "    <toolitem action='TweakDoL' />"
260         "    <toolitem action='TweakDoO' />"
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 *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2714     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2715     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2716         if (doh) gtk_action_set_sensitive (doh, TRUE);
2717         if (dos) gtk_action_set_sensitive (dos, TRUE);
2718         if (dol) gtk_action_set_sensitive (dol, TRUE);
2719         if (doo) gtk_action_set_sensitive (doo, TRUE);
2720         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2721         if (fid) gtk_action_set_sensitive (fid, FALSE);
2722     } else {
2723         if (doh) gtk_action_set_sensitive (doh, FALSE);
2724         if (dos) gtk_action_set_sensitive (dos, FALSE);
2725         if (dol) gtk_action_set_sensitive (dol, FALSE);
2726         if (doo) gtk_action_set_sensitive (doo, FALSE);
2727         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2728         if (fid) gtk_action_set_sensitive (fid, TRUE);
2729     }
2732 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject *tbl )
2734     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2737 static void tweak_toggle_doh (GtkToggleAction *act, gpointer data) {
2738     bool show = gtk_toggle_action_get_active( act );
2739     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2741 static void tweak_toggle_dos (GtkToggleAction *act, gpointer data) {
2742     bool show = gtk_toggle_action_get_active( act );
2743     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2745 static void tweak_toggle_dol (GtkToggleAction *act, gpointer data) {
2746     bool show = gtk_toggle_action_get_active( act );
2747     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2749 static void tweak_toggle_doo (GtkToggleAction *act, gpointer data) {
2750     bool show = gtk_toggle_action_get_active( act );
2751     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
2754 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2756     {
2757         /* Width */
2758         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2759         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2760         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2761                                                               _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2762                                                               "tools.tweak", "width", 15,
2763                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2764                                                               1, 100, 1.0, 10.0,
2765                                                               labels, values, G_N_ELEMENTS(labels),
2766                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2767         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2768         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2769     }
2772     {
2773         /* Force */
2774         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2775         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2776         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2777                                                               _("Force:"), _("The force of the tweak action"),
2778                                                               "tools.tweak", "force", 20,
2779                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2780                                                               1, 100, 1.0, 10.0,
2781                                                               labels, values, G_N_ELEMENTS(labels),
2782                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2783         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2784         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2785     }
2787     {
2788         EgeOutputAction* act = ege_output_action_new( "TweakModeLabel", _("<b>Mode:</b>"), "", 0 );
2789         ege_output_action_set_use_markup( act, TRUE );
2790         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2791     }
2793     /* Mode */
2794     {
2795         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2797         GtkTreeIter iter;
2798         gtk_list_store_append( model, &iter );
2799         gtk_list_store_set( model, &iter,
2800                             0, _("Push mode"),
2801                             1, _("Push parts of paths in any direction"),
2802                             2, "tweak_push_mode",
2803                             -1 );
2805         gtk_list_store_append( model, &iter );
2806         gtk_list_store_set( model, &iter,
2807                             0, _("Shrink mode"),
2808                             1, _("Shrink (inset) parts of paths"),
2809                             2, "tweak_shrink_mode",
2810                             -1 );
2812         gtk_list_store_append( model, &iter );
2813         gtk_list_store_set( model, &iter,
2814                             0, _("Grow mode"),
2815                             1, _("Grow (outset) parts of paths"),
2816                             2, "tweak_grow_mode",
2817                             -1 );
2819         gtk_list_store_append( model, &iter );
2820         gtk_list_store_set( model, &iter,
2821                             0, _("Attract mode"),
2822                             1, _("Attract parts of paths towards cursor"),
2823                             2, "tweak_attract_mode",
2824                             -1 );
2826         gtk_list_store_append( model, &iter );
2827         gtk_list_store_set( model, &iter,
2828                             0, _("Repel mode"),
2829                             1, _("Repel parts of paths from cursor"),
2830                             2, "tweak_repel_mode",
2831                             -1 );
2833         gtk_list_store_append( model, &iter );
2834         gtk_list_store_set( model, &iter,
2835                             0, _("Roughen mode"),
2836                             1, _("Roughen parts of paths"),
2837                             2, "tweak_roughen_mode",
2838                             -1 );
2840         gtk_list_store_append( model, &iter );
2841         gtk_list_store_set( model, &iter,
2842                             0, _("Color paint mode"),
2843                             1, _("Paint the tool's color upon selected objects"),
2844                             2, "tweak_colorpaint_mode",
2845                             -1 );
2847         gtk_list_store_append( model, &iter );
2848         gtk_list_store_set( model, &iter,
2849                             0, _("Color jitter mode"),
2850                             1, _("Jitter the colors of selected objects"),
2851                             2, "tweak_colorjitter_mode",
2852                             -1 );
2854         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
2855         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2856         g_object_set_data( holder, "mode_action", act );
2858         ege_select_one_action_set_appearance( act, "full" );
2859         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2860         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2861         ege_select_one_action_set_icon_column( act, 2 );
2862         ege_select_one_action_set_tooltip_column( act, 1  );
2864         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2865         ege_select_one_action_set_active( act, mode );
2866         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
2868         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
2869     }
2871     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2873     {
2874         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
2875         ege_output_action_set_use_markup( act, TRUE );
2876         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2877         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2878             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2879         g_object_set_data( holder, "tweak_channels_label", act);
2880     }
2882     {
2883         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
2884                                                       _("H"),
2885                                                       _("In color mode, act on objects' hue"),
2886                                                       NULL,
2887                                                       Inkscape::ICON_SIZE_DECORATION );
2888         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2889         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
2890         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
2891         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2892             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2893         g_object_set_data( holder, "tweak_doh", act);
2894     }
2895     {
2896         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
2897                                                       _("S"),
2898                                                       _("In color mode, act on objects' saturation"),
2899                                                       NULL,
2900                                                       Inkscape::ICON_SIZE_DECORATION );
2901         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2902         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
2903         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
2904         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2905             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2906         g_object_set_data( holder, "tweak_dos", act );
2907     }
2908     {
2909         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
2910                                                       _("L"),
2911                                                       _("In color mode, act on objects' lightness"),
2912                                                       NULL,
2913                                                       Inkscape::ICON_SIZE_DECORATION );
2914         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2915         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
2916         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
2917         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2918             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2919         g_object_set_data( holder, "tweak_dol", act );
2920     }
2921     {
2922         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
2923                                                       _("O"),
2924                                                       _("In color mode, act on objects' opacity"),
2925                                                       NULL,
2926                                                       Inkscape::ICON_SIZE_DECORATION );
2927         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2928         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
2929         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
2930         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2931             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2932         g_object_set_data( holder, "tweak_doo", act );
2933     }
2935     {   /* Fidelity */
2936         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
2937         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
2938         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
2939                                                               _("Fidelity:"), _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
2940                                                               "tools.tweak", "fidelity", 50,
2941                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
2942                                                               1, 100, 1.0, 10.0,
2943                                                               labels, values, G_N_ELEMENTS(labels),
2944                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
2945         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2946         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2947         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) 
2948             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
2949         g_object_set_data( holder, "tweak_fidelity", eact );
2950     }
2953     /* Use Pressure button */
2954     {
2955         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
2956                                                       _("Pressure"),
2957                                                       _("Use the pressure of the input device to alter the force of tweak action"),
2958                                                       "use_pressure",
2959                                                       Inkscape::ICON_SIZE_DECORATION );
2960         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2961         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
2962         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
2963     }
2968 //########################
2969 //##     Calligraphy    ##
2970 //########################
2972 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
2974     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2977 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
2979     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2982 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
2984     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2987 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2989     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2992 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
2994     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
2997 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
2999     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3002 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3004     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3007 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3009     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3012 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer data )
3014     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3017 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer data )
3019     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3022 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3024     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3026     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3029 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3031     // FIXME: make defaults settable via Inkscape Options
3032     struct KeyValue {
3033         char const *key;
3034         double value;
3035     } const key_values[] = {
3036         {"mass", 0.02},
3037         {"wiggle", 0.0},
3038         {"angle", 30.0},
3039         {"width", 15},
3040         {"thinning", 0.1},
3041         {"tremor", 0.0},
3042         {"flatness", 0.9},
3043         {"cap_rounding", 0.0}
3044     };
3046     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3047         KeyValue const &kv = key_values[i];
3048         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3049         if ( adj ) {
3050             gtk_adjustment_set_value(adj, kv.value);
3051         }
3052     }
3056 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3058     {
3059         EgeAdjustmentAction* calligraphy_angle = 0;
3061         {
3062         /* Width */
3063         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3064         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3065         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3066                                          _("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
3067                                          "tools.calligraphic", "width", 15,
3068                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3069                                          1, 100, 1.0, 10.0,
3070                                          labels, values, G_N_ELEMENTS(labels),
3071                                          sp_ddc_width_value_changed,  0.01, 0, 100 );
3072         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3073         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3074         }
3076         {
3077         /* Thinning */
3078             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3079             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3080         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3081                                          _("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3082                                          "tools.calligraphic", "thinning", 0.1,
3083                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3084                                          -1.0, 1.0, 0.01, 0.1,
3085                                          labels, values, G_N_ELEMENTS(labels),
3086                                          sp_ddc_velthin_value_changed, 0.01, 2);
3087         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3088         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3089         }
3091         {
3092         /* Angle */
3093         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3094         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3095         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3096                                          _("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3097                                          "tools.calligraphic", "angle", 30,
3098                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3099                                          -90.0, 90.0, 1.0, 10.0,
3100                                          labels, values, G_N_ELEMENTS(labels),
3101                                          sp_ddc_angle_value_changed, 1, 0 );
3102         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3103         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3104         calligraphy_angle = eact;
3105         }
3107         {
3108         /* Fixation */
3109             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3110         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3111         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3112                                          _("Fixation:"), _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3113                                          "tools.calligraphic", "flatness", 0.9,
3114                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3115                                          0.0, 1.0, 0.01, 0.1,
3116                                          labels, values, G_N_ELEMENTS(labels),
3117                                          sp_ddc_flatness_value_changed, 0.01, 2 );
3118         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3119         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3120         }
3122         {
3123         /* Cap Rounding */
3124             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3125         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3126         // TRANSLATORS: "cap" means "end" (both start and finish) here
3127         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3128                                          _("Caps:"), _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3129                                          "tools.calligraphic", "cap_rounding", 0.0,
3130                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3131                                          0.0, 5.0, 0.01, 0.1,
3132                                          labels, values, G_N_ELEMENTS(labels),
3133                                          sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3134         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3135         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3136         }
3138         {
3139         /* Tremor */
3140             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3141         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3142         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3143                                          _("Tremor:"), _("Increase to make strokes rugged and trembling"),
3144                                          "tools.calligraphic", "tremor", 0.0,
3145                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3146                                          0.0, 1.0, 0.01, 0.1,
3147                                          labels, values, G_N_ELEMENTS(labels),
3148                                          sp_ddc_tremor_value_changed, 0.01, 2 );
3150         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3151         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3152         }
3154         {
3155         /* Wiggle */
3156         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3157         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3158         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3159                                          _("Wiggle:"), _("Increase to make the pen waver and wiggle"),
3160                                          "tools.calligraphic", "wiggle", 0.0,
3161                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3162                                          0.0, 1.0, 0.01, 0.1,
3163                                          labels, values, G_N_ELEMENTS(labels),
3164                                          sp_ddc_wiggle_value_changed, 0.01, 2 );
3165         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3166         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3167         }
3169         {
3170         /* Mass */
3171             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3172         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3173         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3174                                          _("Mass:"), _("Increase to make the pen drag behind, as if slowed by inertia"),
3175                                          "tools.calligraphic", "mass", 0.02,
3176                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3177                                          0.0, 1.0, 0.01, 0.1,
3178                                          labels, values, G_N_ELEMENTS(labels),
3179                                          sp_ddc_mass_value_changed, 0.01, 2 );
3180         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3181         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3182         }
3185         /* Trace Background button */
3186         {
3187             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3188                                                           _("Trace Background"),
3189                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3190                                                           "trace_background",
3191                                                           Inkscape::ICON_SIZE_DECORATION );
3192             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3193             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3194             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3195         }
3197         /* Use Pressure button */
3198         {
3199             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3200                                                           _("Pressure"),
3201                                                           _("Use the pressure of the input device to alter the width of the pen"),
3202                                                           "use_pressure",
3203                                                           Inkscape::ICON_SIZE_DECORATION );
3204             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3205             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3206             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3207         }
3209         /* Use Tilt button */
3210         {
3211             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3212                                                           _("Tilt"),
3213                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3214                                                           "use_tilt",
3215                                                           Inkscape::ICON_SIZE_DECORATION );
3216             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3217             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3218             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3219             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3220         }
3222         /* Reset */
3223         {
3224             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3225                                              _("Defaults"),
3226                                              _("Reset all parameters to defaults"),
3227                                              GTK_STOCK_CLEAR );
3228             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3229             gtk_action_group_add_action( mainActions, act );
3230             gtk_action_set_sensitive( act, TRUE );
3231         }
3232     }
3236 //########################
3237 //##    Circle / Arc    ##
3238 //########################
3240 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3242     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3243     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3245     if (v1 == 0 && v2 == 0) {
3246         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3247             gtk_action_set_sensitive( ocb, FALSE );
3248             gtk_action_set_sensitive( make_whole, FALSE );
3249         }
3250     } else {
3251         gtk_action_set_sensitive( ocb, TRUE );
3252         gtk_action_set_sensitive( make_whole, TRUE );
3253     }
3256 static void
3257 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3259     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3261     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3262         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3263     }
3265     // quit if run by the attr_changed listener
3266     if (g_object_get_data( tbl, "freeze" )) {
3267         return;
3268     }
3270     // in turn, prevent listener from responding
3271     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3273     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3275     bool modmade = false;
3276     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3277          items != NULL;
3278          items = items->next)
3279     {
3280         SPItem *item = SP_ITEM(items->data);
3282         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3284             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3285             SPArc *arc = SP_ARC(item);
3287             if (!strcmp(value_name, "start"))
3288                 ge->start = (adj->value * M_PI)/ 180;
3289             else
3290                 ge->end = (adj->value * M_PI)/ 180;
3292             sp_genericellipse_normalize(ge);
3293             ((SPObject *)arc)->updateRepr();
3294             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3296             modmade = true;
3297         }
3298     }
3300     g_free(namespaced_name);
3302     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3304     sp_arctb_sensitivize( tbl, adj->value, other->value );
3306     if (modmade) {
3307         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3308                                    _("Arc: Change start/end"));
3309     }
3311     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3315 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3317     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3320 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3322     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3325 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3327     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3328     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3329         if ( ege_select_one_action_get_active( act ) != 0 ) {
3330             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3331         } else {
3332             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3333         }
3334     }
3336     // quit if run by the attr_changed listener
3337     if (g_object_get_data( tbl, "freeze" )) {
3338         return;
3339     }
3341     // in turn, prevent listener from responding
3342     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3344     bool modmade = false;
3346     if ( ege_select_one_action_get_active(act) != 0 ) {
3347         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3348              items != NULL;
3349              items = items->next)
3350         {
3351             if (SP_IS_ARC((SPItem *) items->data)) {
3352                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3353                 repr->setAttribute("sodipodi:open", "true");
3354                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3355                 modmade = true;
3356             }
3357         }
3358     } else {
3359         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3360              items != NULL;
3361              items = items->next)
3362         {
3363             if (SP_IS_ARC((SPItem *) items->data))    {
3364                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3365                 repr->setAttribute("sodipodi:open", NULL);
3366                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3367                 modmade = true;
3368             }
3369         }
3370     }
3372     if (modmade) {
3373         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3374                                    _("Arc: Change open/closed"));
3375     }
3377     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3380 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3382     GtkAdjustment *adj;
3383     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3384     gtk_adjustment_set_value(adj, 0.0);
3385     gtk_adjustment_value_changed(adj);
3387     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3388     gtk_adjustment_set_value(adj, 0.0);
3389     gtk_adjustment_value_changed(adj);
3391     spinbutton_defocus( GTK_OBJECT(obj) );
3394 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
3395                                       gchar const *old_value, gchar const *new_value,
3396                                       bool is_interactive, gpointer data)
3398     GObject *tbl = G_OBJECT(data);
3400     // quit if run by the _changed callbacks
3401     if (g_object_get_data( tbl, "freeze" )) {
3402         return;
3403     }
3405     // in turn, prevent callbacks from responding
3406     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3408     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3409     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3411     GtkAdjustment *adj1,*adj2;
3412     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3413     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3414     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3415     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3417     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3419     char const *openstr = NULL;
3420     openstr = repr->attribute("sodipodi:open");
3421     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3423     if (openstr) {
3424         ege_select_one_action_set_active( ocb, 1 );
3425     } else {
3426         ege_select_one_action_set_active( ocb, 0 );
3427     }
3429     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3432 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3433     NULL, /* child_added */
3434     NULL, /* child_removed */
3435     arc_tb_event_attr_changed,
3436     NULL, /* content_changed */
3437     NULL  /* order_changed */
3438 };
3441 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3443     int n_selected = 0;
3444     Inkscape::XML::Node *repr = NULL;
3446     purge_repr_listener( tbl, tbl );
3448     for (GSList const *items = selection->itemList();
3449          items != NULL;
3450          items = items->next)
3451     {
3452         if (SP_IS_ARC((SPItem *) items->data)) {
3453             n_selected++;
3454             repr = SP_OBJECT_REPR((SPItem *) items->data);
3455         }
3456     }
3458     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3460     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3461     if (n_selected == 0) {
3462         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3463     } else if (n_selected == 1) {
3464         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3465         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3467         if (repr) {
3468             g_object_set_data( tbl, "repr", repr );
3469             Inkscape::GC::anchor(repr);
3470             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3471             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3472         }
3473     } else {
3474         // FIXME: implement averaging of all parameters for multiple selected
3475         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3476         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3477         sp_arctb_sensitivize( tbl, 1, 0 );
3478     }
3482 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3484     EgeAdjustmentAction* eact = 0;
3487     {
3488         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3489         ege_output_action_set_use_markup( act, TRUE );
3490         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3491         g_object_set_data( holder, "mode_action", act );
3492     }
3494     /* Start */
3495     {
3496         eact = create_adjustment_action( "ArcStartAction",
3497                                          _("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
3498                                          "tools.shapes.arc", "start", 0.0,
3499                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3500                                          -360.0, 360.0, 1.0, 10.0,
3501                                          0, 0, 0,
3502                                          sp_arctb_start_value_changed);
3503         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3504     }
3506     /* End */
3507     {
3508         eact = create_adjustment_action( "ArcEndAction",
3509                                          _("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
3510                                          "tools.shapes.arc", "end", 0.0,
3511                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3512                                          -360.0, 360.0, 1.0, 10.0,
3513                                          0, 0, 0,
3514                                          sp_arctb_end_value_changed);
3515         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3516     }
3518     /* Segments / Pie checkbox */
3519     {
3520         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3522         GtkTreeIter iter;
3523         gtk_list_store_append( model, &iter );
3524         gtk_list_store_set( model, &iter,
3525                             0, _("Closed arc"),
3526                             1, _("Switch to segment (closed shape with two radii)"),
3527                             2, "circle_closed_arc",
3528                             -1 );
3530         gtk_list_store_append( model, &iter );
3531         gtk_list_store_set( model, &iter,
3532                             0, _("Open Arc"),
3533                             1, _("Switch to arc (unclosed shape)"),
3534                             2, "circle_open_arc",
3535                             -1 );
3537         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
3538         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3539         g_object_set_data( holder, "open_action", act );
3541         ege_select_one_action_set_appearance( act, "full" );
3542         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3543         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3544         ege_select_one_action_set_icon_column( act, 2 );
3545         ege_select_one_action_set_tooltip_column( act, 1  );
3547         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3548         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3549         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3550         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3551     }
3553     /* Make Whole */
3554     {
3555         InkAction* inky = ink_action_new( "ArcResetAction",
3556                                           _("Make whole"),
3557                                           _("Make the shape a whole ellipse, not arc or segment"),
3558                                           "reset_circle",
3559                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3560         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3561         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3562         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3563         g_object_set_data( holder, "make_whole", inky );
3564     }
3566     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3567     // sensitivize make whole and open checkbox
3568     {
3569         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3570         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3571         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3572     }
3575     sigc::connection *connection = new sigc::connection(
3576         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3577         );
3578     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3579     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3585 // toggle button callbacks and updaters
3587 //########################
3588 //##      Dropper       ##
3589 //########################
3591 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3592     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3593     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3594     if ( set_action ) {
3595         if ( gtk_toggle_action_get_active( act ) ) {
3596             gtk_action_set_sensitive( set_action, TRUE );
3597         } else {
3598             gtk_action_set_sensitive( set_action, FALSE );
3599         }
3600     }
3602     spinbutton_defocus(GTK_OBJECT(tbl));
3605 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3606     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3607     spinbutton_defocus(GTK_OBJECT(tbl));
3611 /**
3612  * Dropper auxiliary toolbar construction and setup.
3613  *
3614  * TODO: Would like to add swatch of current color.
3615  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3616  *       can drag and drop places. Will provide a nice mixing palette.
3617  */
3618 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3620     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3622     {
3623         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3624                                                       _("Pick alpha"),
3625                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3626                                                       "color_alpha_get",
3627                                                       Inkscape::ICON_SIZE_DECORATION );
3628         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3629         g_object_set_data( holder, "pick_action", act );
3630         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3631         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3632     }
3634     {
3635         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3636                                                       _("Set alpha"),
3637                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3638                                                       "color_alpha_set",
3639                                                       Inkscape::ICON_SIZE_DECORATION );
3640         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3641         g_object_set_data( holder, "set_action", act );
3642         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3643         // make sure it's disabled if we're not picking alpha
3644         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3645         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3646     }
3650 //########################
3651 //##    Text Toolbox    ##
3652 //########################
3653 /*
3654 static void
3655 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3657     //Call back for letter sizing spinbutton
3660 static void
3661 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3663     //Call back for line height spinbutton
3666 static void
3667 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3669     //Call back for horizontal kerning spinbutton
3672 static void
3673 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3675     //Call back for vertical kerning spinbutton
3678 static void
3679 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3681     //Call back for letter rotation spinbutton
3682 }*/
3684 namespace {
3686 bool visible = false;
3688 void
3689 sp_text_toolbox_selection_changed (Inkscape::Selection *selection, GObject *tbl)
3691     SPStyle *query =
3692         sp_style_new (SP_ACTIVE_DOCUMENT);
3694     int result_family =
3695         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3697     int result_style =
3698         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3700     int result_numbers =
3701         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3703     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3705     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3706     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3707     {
3708         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3710         if (repr)
3711         {
3712             sp_style_read_from_repr (query, repr);
3713         }
3714         else
3715         {
3716             return;
3717         }
3718     }
3720     if (query->text)
3721     {
3722         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3723             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3724             gtk_entry_set_text (GTK_ENTRY (entry), "");
3726         } else if (query->text->font_family.value) {
3728             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3729             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3731             Gtk::TreePath path;
3732             try {
3733                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3734             } catch (...) {
3735                 return;
3736             }
3738             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3739             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3741             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3743             gtk_tree_selection_select_path (tselection, path.gobj());
3744             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3746             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3747         }
3749         //Size
3750         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3751         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3752         g_object_set_data (tbl, "size-block", gpointer(1));
3753         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3754         g_object_set_data (tbl, "size-block", gpointer(0));
3755         free (str);
3757         //Anchor
3758         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3759         {
3760             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3761             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3762             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3763             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3764         }
3765         else
3766         {
3767             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3768             {
3769                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3770                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3771                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3772                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3773             }
3774             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3775             {
3776                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3777                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3778                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3779                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3780             }
3781             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3782             {
3783                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3784                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3785                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3786                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3787             }
3788         }
3790         //Style
3791         {
3792             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3794             gboolean active = gtk_toggle_button_get_active (button);
3795             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3797             if (active != check)
3798             {
3799                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3800                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3801                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3802             }
3803         }
3805         {
3806             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3808             gboolean active = gtk_toggle_button_get_active (button);
3809             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3811             if (active != check)
3812             {
3813                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3814                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3815                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3816             }
3817         }
3819         //Orientation
3820         //locking both buttons, changing one affect all group (both)
3821         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3822         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3824         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3825         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3827         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3828         {
3829             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3830         }
3831         else
3832         {
3833             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3834         }
3835         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3836         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3837     }
3839     sp_style_unref(query);
3842 void
3843 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint flags, GObject *tbl)
3845     sp_text_toolbox_selection_changed (selection, tbl);
3848 void
3849 sp_text_toolbox_subselection_changed (gpointer dragger, GObject *tbl)
3851     sp_text_toolbox_selection_changed (NULL, tbl);
3854 void
3855 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3856                                 GObject             *tbl)
3858     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3859     GtkTreeModel *model = 0;
3860     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3861     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3862     GtkTreeIter   iter;
3863     char         *family = 0;
3865     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3866     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3868     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3869         return;
3870     }
3872     gtk_tree_model_get (model, &iter, 0, &family, -1);
3874     if (g_object_get_data (G_OBJECT (selection), "block"))
3875     {
3876         gtk_entry_set_text (GTK_ENTRY (entry), family);
3877         return;
3878     }
3880     gtk_widget_hide (popdown);
3881     visible = false;
3883     gtk_entry_set_text (GTK_ENTRY (entry), family);
3885     SPStyle *query =
3886         sp_style_new (SP_ACTIVE_DOCUMENT);
3888     int result_numbers =
3889         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3891     SPCSSAttr *css = sp_repr_css_attr_new ();
3892     sp_repr_css_set_property (css, "font-family", family);
3894     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3895     if (result_numbers == QUERY_STYLE_NOTHING)
3896     {
3897         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3898         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3899     }
3900     else
3901     {
3902         sp_desktop_set_style (desktop, css, true, true);
3903     }
3905     sp_style_unref(query);
3907     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3908                                    _("Text: Change font family"));
3909     sp_repr_css_attr_unref (css);
3910     free (family);
3911     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3913     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3916 void
3917 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3918                                        GObject      *tbl)
3920     const char *family = gtk_entry_get_text (entry);
3922     try {
3923         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3924         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3925         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3926         gtk_tree_selection_select_path (selection, path.gobj());
3927         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3928         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3929     } catch (...) {
3930         if (family && strlen (family))
3931         {
3932             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3933         }
3934     }
3937 void
3938 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3939                                    gpointer          data)
3941     if (g_object_get_data (G_OBJECT (button), "block")) return;
3942     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3943     int prop = GPOINTER_TO_INT(data);
3945     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3946     SPCSSAttr *css = sp_repr_css_attr_new ();
3948     switch (prop)
3949     {
3950         case 0:
3951         {
3952             sp_repr_css_set_property (css, "text-anchor", "start");
3953             sp_repr_css_set_property (css, "text-align", "start");
3954             break;
3955         }
3956         case 1:
3957         {
3958             sp_repr_css_set_property (css, "text-anchor", "middle");
3959             sp_repr_css_set_property (css, "text-align", "center");
3960             break;
3961         }
3963         case 2:
3964         {
3965             sp_repr_css_set_property (css, "text-anchor", "end");
3966             sp_repr_css_set_property (css, "text-align", "end");
3967             break;
3968         }
3970         case 3:
3971         {
3972             sp_repr_css_set_property (css, "text-anchor", "start");
3973             sp_repr_css_set_property (css, "text-align", "justify");
3974             break;
3975         }
3976     }
3978     SPStyle *query =
3979         sp_style_new (SP_ACTIVE_DOCUMENT);
3980     int result_numbers =
3981         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3983     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3984     if (result_numbers == QUERY_STYLE_NOTHING)
3985     {
3986         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3987     }
3989     sp_style_unref(query);
3991     sp_desktop_set_style (desktop, css, true, true);
3992     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3993                                    _("Text: Change alignment"));
3994     sp_repr_css_attr_unref (css);
3996     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3999 void
4000 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4001                                gpointer          data)
4003     if (g_object_get_data (G_OBJECT (button), "block")) return;
4005     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4006     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4007     int          prop       = GPOINTER_TO_INT(data);
4008     bool         active     = gtk_toggle_button_get_active (button);
4011     switch (prop)
4012     {
4013         case 0:
4014         {
4015             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4016             break;
4017         }
4019         case 1:
4020         {
4021             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4022             break;
4023         }
4024     }
4026     SPStyle *query =
4027         sp_style_new (SP_ACTIVE_DOCUMENT);
4028     int result_numbers =
4029         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4031     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4032     if (result_numbers == QUERY_STYLE_NOTHING)
4033     {
4034         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4035     }
4037     sp_style_unref(query);
4039     sp_desktop_set_style (desktop, css, true, true);
4040     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4041                                    _("Text: Change font style"));
4042     sp_repr_css_attr_unref (css);
4044     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4047 void
4048 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4049                                      gpointer         data)
4051     if (g_object_get_data (G_OBJECT (button), "block")) {
4052         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4053         return;
4054     }
4056     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4057     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4058     int          prop       = GPOINTER_TO_INT(data);
4060     switch (prop)
4061     {
4062         case 0:
4063         {
4064             sp_repr_css_set_property (css, "writing-mode", "lr");
4065             break;
4066         }
4068         case 1:
4069         {
4070             sp_repr_css_set_property (css, "writing-mode", "tb");
4071             break;
4072         }
4073     }
4075     SPStyle *query =
4076         sp_style_new (SP_ACTIVE_DOCUMENT);
4077     int result_numbers =
4078         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4080     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4081     if (result_numbers == QUERY_STYLE_NOTHING)
4082     {
4083         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4084     }
4086     sp_desktop_set_style (desktop, css, true, true);
4087     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4088                                    _("Text: Change orientation"));
4089     sp_repr_css_attr_unref (css);
4091     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4094 gboolean
4095 sp_text_toolbox_size_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
4097     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4098     if (!desktop) return FALSE;
4100     switch (get_group0_keyval (event)) {
4101         case GDK_Escape: // defocus
4102             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4103             return TRUE; // I consumed the event
4104             break;
4105         case GDK_Return: // defocus
4106         case GDK_KP_Enter:
4107             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4108             return TRUE; // I consumed the event
4109             break;
4110     }
4111     return FALSE;
4114 gboolean
4115 sp_text_toolbox_family_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
4117     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4118     if (!desktop) return FALSE;
4120     switch (get_group0_keyval (event)) {
4121         case GDK_Escape: // defocus
4122             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4123             sp_text_toolbox_selection_changed (NULL, tbl); // update
4124             return TRUE; // I consumed the event
4125             break;
4126     }
4127     return FALSE;
4130 gboolean
4131 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
4133     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4134     if (!desktop) return FALSE;
4136     switch (get_group0_keyval (event)) {
4137         case GDK_Escape: // defocus
4138             gtk_widget_hide (w);
4139             visible = false;
4140             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4141             return TRUE; // I consumed the event
4142             break;
4143     }
4144     return FALSE;
4148 void
4149 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4150                                GObject     *tbl)
4152     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4154     if (g_object_get_data (tbl, "size-block")) return;
4156 #if GTK_CHECK_VERSION(2,6,0)
4157     char *text = gtk_combo_box_get_active_text (cbox);
4158 #else // GTK_CHECK_VERSION(2,6,0)
4159     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
4160     GtkTreeIter iter;
4161     char *text = NULL;
4163     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
4164         gtk_tree_model_get (model, &iter, 0, &text, -1);
4165 #endif // GTK_CHECK_VERSION(2,6,0)
4167     SPCSSAttr *css = sp_repr_css_attr_new ();
4168     sp_repr_css_set_property (css, "font-size", text);
4169     free (text);
4171     SPStyle *query =
4172         sp_style_new (SP_ACTIVE_DOCUMENT);
4173     int result_numbers =
4174         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4176     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4177     if (result_numbers == QUERY_STYLE_NOTHING)
4178     {
4179         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4180     }
4182     sp_style_unref(query);
4184     sp_desktop_set_style (desktop, css, true, true);
4185     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4186                                    _("Text: Change font size"));
4187     sp_repr_css_attr_unref (css);
4190     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4191         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4194 void
4195 sp_text_toolbox_text_popdown_clicked    (GtkButton          *button,
4196                                          GObject            *tbl)
4198     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4199     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4200     int x, y;
4202     if (!visible)
4203     {
4204         gdk_window_get_origin (widget->window, &x, &y);
4205         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4206         gtk_widget_show_all (popdown);
4208         gdk_pointer_grab (widget->window, TRUE,
4209                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4210                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4211                                         GDK_POINTER_MOTION_MASK),
4212                           NULL, NULL, GDK_CURRENT_TIME);
4214         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4216         visible = true;
4217     }
4218     else
4219     {
4220         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4221         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4222         gtk_widget_hide (popdown);
4223         visible = false;
4224     }
4227 gboolean
4228 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4229                                  GdkEventFocus    *event,
4230                                  GObject          *tbl)
4232     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4233     return FALSE;
4236 gboolean
4237 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4238                                    GdkEventFocus    *event,
4239                                    GObject          *tbl)
4241     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4243     gtk_widget_hide (popdown);
4244     visible = false;
4245     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4246     return TRUE;
4249 void
4250 cell_data_func  (GtkTreeViewColumn *column,
4251                  GtkCellRenderer   *cell,
4252                  GtkTreeModel      *tree_model,
4253                  GtkTreeIter       *iter,
4254                  gpointer           data)
4256     char        *family,
4257         *family_escaped,
4258         *sample_escaped;
4260     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4262     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4264     family_escaped = g_markup_escape_text (family, -1);
4265     sample_escaped = g_markup_escape_text (sample, -1);
4267     std::stringstream markup;
4268     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4269     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4271     free (family);
4272     free (family_escaped);
4273     free (sample_escaped);
4276 static void delete_completion(GObject *obj, GtkWidget *entry) {
4277     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4278     if (completion) {
4279         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4280         g_object_unref (completion);
4281     }
4284 GtkWidget*
4285 sp_text_toolbox_new (SPDesktop *desktop)
4287     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4289     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4290     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4292     GtkTooltips *tt = gtk_tooltips_new();
4293     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4295     ////////////Family
4296     //Window
4297     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4298     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4300     //Entry
4301     GtkWidget           *entry = gtk_entry_new ();
4302     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4303     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4304     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4305     gtk_entry_completion_set_text_column (completion, 0);
4306     gtk_entry_completion_set_minimum_key_length (completion, 1);
4307     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4308     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4309     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4310     aux_toolbox_space (tbl, 1);
4311     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4312     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4314     //Button
4315     GtkWidget   *button = gtk_button_new ();
4316     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4317     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4319     //Popdown
4320     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4321     GtkWidget           *treeview = gtk_tree_view_new ();
4323     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4324     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4325     gtk_tree_view_column_pack_start (column, cell, FALSE);
4326     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4327     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4328     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4330     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4331     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4332 #if GTK_CHECK_VERSION(2,6,0)
4333     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4334 #endif // GTK_CHECK_VERSION(2,6,0)
4336     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4338     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4339     gtk_container_add (GTK_CONTAINER (sw), treeview);
4341     gtk_container_add (GTK_CONTAINER (window), sw);
4342     gtk_widget_set_size_request (window, 300, 450);
4344     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4345     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4346     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4348     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4350     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4351     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4353     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4354     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4356     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4357     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4358     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4359     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4360     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4362     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4363     aux_toolbox_space (tbl, 1);
4364     GtkWidget *box = gtk_event_box_new ();
4365     gtk_container_add (GTK_CONTAINER (box), image);
4366     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4367     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4368     GtkTooltips *tooltips = gtk_tooltips_new ();
4369     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4370     gtk_widget_hide (GTK_WIDGET (box));
4371     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4373     ////////////Size
4374     const char *sizes[] = {
4375         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4376         "16", "18", "20", "22", "24", "28",
4377         "32", "36", "40", "48", "56", "64", "72", "144"
4378     };
4380     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4381     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4382     gtk_widget_set_size_request (cbox, 80, -1);
4383     aux_toolbox_space (tbl, 1);
4384     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4385     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4386     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4387     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4389     //spacer
4390     aux_toolbox_space (tbl, 4);
4391     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4393     ////////////Text anchor
4394     GtkWidget *group   = gtk_radio_button_new (NULL);
4395     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4396     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4398     // left
4399     GtkWidget *rbutton = group;
4400     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4401     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4402     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4404     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4405     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4406     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4407     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4409     // center
4410     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4411     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4412     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4413     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4415     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4416     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4417     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4418     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4420     // right
4421     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4422     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4423     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4424     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4426     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4427     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4428     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4429     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4431     // fill
4432     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4433     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4434     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4435     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4437     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4438     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4439     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4440     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4442     aux_toolbox_space (tbl, 1);
4443     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4445     //spacer
4446     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4448     ////////////Text style
4449     row = gtk_hbox_new (FALSE, 4);
4451     // bold
4452     rbutton = gtk_toggle_button_new ();
4453     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4454     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4455     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4456     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4458     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4459     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4460     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4462     // italic
4463     rbutton = gtk_toggle_button_new ();
4464     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4465     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4466     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4467     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4469     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4470     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4471     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4473     aux_toolbox_space (tbl, 1);
4474     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4476     //spacer
4477     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4479     ////////////Text orientation
4480     group   = gtk_radio_button_new (NULL);
4481     row     = gtk_hbox_new (FALSE, 4);
4482     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4484     // horizontal
4485     rbutton = group;
4486     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4487     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4488     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4489     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4491     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4492     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4493     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4495     // vertical
4496     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4497     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4498     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4499     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4500     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4502     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4503     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4504     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4505     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4508     //watch selection
4509     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4511     sigc::connection *c_selection_changed =
4512         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4513                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4514     pool->add_connection ("selection-changed", c_selection_changed);
4516     sigc::connection *c_selection_modified =
4517         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4518                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4519     pool->add_connection ("selection-modified", c_selection_modified);
4521     sigc::connection *c_subselection_changed =
4522         new sigc::connection (desktop->connectToolSubselectionChanged
4523                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4524     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4526     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4529 #if 0
4530     // horizontal
4531     {
4532         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4533         GtkWidget *b = group = gtk_radio_button_new (NULL);
4534         gtk_container_add (GTK_CONTAINER (b), px);
4535         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4536         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4537         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4538         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4539     }
4541     // vertical
4542     {
4543         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4544         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4545         gtk_container_add (GTK_CONTAINER (b), px);
4546         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4547         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4548         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4549         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4550     }
4552     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4554     // letter spacing
4555     {
4556         {
4557             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4558             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4559             gtk_container_add (GTK_CONTAINER (hb), image);
4560             gtk_widget_show(image);
4561             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4562         }
4564         {
4565             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between letters"),
4566                                              "tools.text", "letter_spacing", 0.0,
4567                                              us, tbl, FALSE, NULL,
4568                                              -1000.0, 1000.0, 0.1, 0.1,
4569                                              sp_text_letter_changed, 0.1, 1);
4570             gtk_widget_set_size_request (hb, 45, 6);
4571             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4572         }
4573     }
4575     // line spacing
4576     {
4577         {
4578             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4579             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4580             gtk_container_add (GTK_CONTAINER (hb), image);
4581             gtk_widget_show(image);
4582             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4583         }
4585         {
4586             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between lines"),
4587                                              "tools.text", "line_spacing", 0,
4588                                              us, tbl, FALSE, NULL,
4589                                              -1000.0, 1000.0, 0.1, 0.1,
4590                                              sp_text_line_changed, 0.1, 1);
4591             gtk_widget_set_size_request (hb, 45, 0);
4592             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4593         }
4594     }
4596     {
4597         // horizontal kerning/vertical kerning units menu: create
4598         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4599         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4600         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4602         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4604         // horizontal kerning
4605         {
4606             {
4607                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4608                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4609                 gtk_container_add (GTK_CONTAINER (hb), image);
4610                 gtk_widget_show(image);
4611                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4612             }
4614             {
4615                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Horizontal kerning"),
4616                                                  "tools.text", "horizontal_kerning", 0,
4617                                                  us, tbl, FALSE, NULL,
4618                                                  -100.00, 100.00, 0.01, 0.1,
4619                                                  sp_text_horiz_kern_changed);
4620                 gtk_widget_set_size_request (hb, 45, 0);
4621                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4622             }
4623         }
4625         // vertical kerning
4626         {
4627             {
4628                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4629                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4630                 gtk_container_add (GTK_CONTAINER (hb), image);
4631                 gtk_widget_show(image);
4632                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4633             }
4635             {
4636                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Vertical kerning"),
4637                                                  "tools.text", "vertical_kerning", 0,
4638                                                  us, tbl, FALSE, NULL,
4639                                                  -100.00, 100.00, 0.01, 0.1,
4640                                                  sp_text_vert_kern_changed);
4641                 gtk_widget_set_size_request (hb, 45, 0);
4642                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4643             }
4644         }
4646         // add the units menu
4647         gtk_widget_show(us);
4648         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4649         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4650     }
4652     // letter rotation
4653     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4654     {
4655         {
4656             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4657             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4658             gtk_container_add (GTK_CONTAINER (hb), image);
4659             gtk_widget_show(image);
4660             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4661         }
4662         {
4663             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Letter rotation"),
4664                                              "tools.text", "letter_rotation", 0,
4665                                              us, tbl, FALSE, NULL,
4666                                              -180.0, 180.0, 0.1, 0.1,
4667                                              sp_text_letter_rotation_changed, 0.1, 1);
4668             gtk_widget_set_size_request (hb, 45, 0);
4669             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4670         }
4671         // rotation degree label
4672         {
4673             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4674             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4675         }
4676     }
4678     // Remove Manual Kerns
4679     {
4680         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4681         GtkWidget *button = gtk_button_new ();
4682         gtk_container_add (GTK_CONTAINER (button), px);
4683         gtk_widget_show(button);
4684         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4685         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4686         gtk_widget_set_sensitive(button, TRUE);
4687         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4688     }
4689 #endif
4691     gtk_widget_show_all (tbl);
4692     return tbl;
4694 } // end of sp_text_toolbox_new()
4696 }//<unnamed> namespace
4699 //#########################
4700 //##  Connector Toolbox  ##
4701 //#########################
4703 static void sp_connector_path_set_avoid(void)
4705     cc_selection_set_avoid(true);
4709 static void sp_connector_path_set_ignore(void)
4711     cc_selection_set_avoid(false);
4716 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4718     // quit if run by the _changed callbacks
4719     if (g_object_get_data( tbl, "freeze" )) {
4720         return;
4721     }
4723     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4724     SPDocument *doc = sp_desktop_document(desktop);
4726     if (!sp_document_get_undo_sensitive(doc))
4727     {
4728         return;
4729     }
4731     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4733     if ( repr->attribute("inkscape:connector-spacing") ) {
4734         gdouble priorValue = gtk_adjustment_get_value(adj);
4735         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4736         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4737             return;
4738         }
4739     } else if ( adj->value == defaultConnSpacing ) {
4740         return;
4741     }
4743     // in turn, prevent callbacks from responding
4744     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4746     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4747     SP_OBJECT(desktop->namedview)->updateRepr();
4749     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4750     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4751         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4752         NR::Matrix m = NR::identity();
4753         avoid_item_move(&m, item);
4754     }
4756     if (items) {
4757         g_slist_free(items);
4758     }
4760     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4761             _("Change connector spacing"));
4763     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4765     spinbutton_defocus(GTK_OBJECT(tbl));
4768 static void sp_connector_graph_layout(void)
4770     if (!SP_ACTIVE_DESKTOP) return;
4772     // hack for clones, see comment in align-and-distribute.cpp
4773     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4774     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4776     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4778     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4780     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4783 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4785     if ( gtk_toggle_action_get_active( act ) ) {
4786         prefs_set_string_attribute("tools.connector", "directedlayout",
4787                 "true");
4788     } else {
4789         prefs_set_string_attribute("tools.connector", "directedlayout",
4790                 "false");
4791     }
4794 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4796     if ( gtk_toggle_action_get_active( act ) ) {
4797         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4798                 "true");
4799     } else {
4800         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4801                 "false");
4802     }
4806 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4808     prefs_set_double_attribute("tools.connector", "length", adj->value);
4809     spinbutton_defocus(GTK_OBJECT(tbl));
4812 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4813         gchar const *name, gchar const *old_value, gchar const *new_value,
4814         bool is_interactive, gpointer data)
4816     GtkWidget *tbl = GTK_WIDGET(data);
4818     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4819         return;
4820     }
4821     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4822         return;
4823     }
4825     GtkAdjustment *adj = (GtkAdjustment*)
4826             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4827     gdouble spacing = defaultConnSpacing;
4828     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4830     gtk_adjustment_set_value(adj, spacing);
4834 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4835     NULL, /* child_added */
4836     NULL, /* child_removed */
4837     connector_tb_event_attr_changed,
4838     NULL, /* content_changed */
4839     NULL  /* order_changed */
4840 };
4843 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4845     {
4846         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4847                                           _("Avoid"),
4848                                           _("Make connectors avoid selected objects"),
4849                                           "connector_avoid",
4850                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4851         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
4852         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4853     }
4855     {
4856         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
4857                                           _("Ignore"),
4858                                           _("Make connectors ignore selected objects"),
4859                                           "connector_ignore",
4860                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4861         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
4862         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4863     }
4865     EgeAdjustmentAction* eact = 0;
4867     // Spacing spinbox
4868     eact = create_adjustment_action( "ConnectorSpacingAction",
4869                                      _("Spacing:"), _("The amount of space left around objects by auto-routing connectors"),
4870                                      "tools.connector", "spacing", defaultConnSpacing,
4871                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
4872                                      0, 100, 1.0, 10.0,
4873                                      0, 0, 0,
4874                                      connector_spacing_changed, 1, 0 );
4875     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4877     // Graph (connector network) layout
4878     {
4879         InkAction* inky = ink_action_new( "ConnectorGraphAction",
4880                                           _("Graph"),
4881                                           _("Nicely arrange selected connector network"),
4882                                           "graph_layout",
4883                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4884         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
4885         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4886     }
4888     // Default connector length spinbox
4889     eact = create_adjustment_action( "ConnectorLengthAction",
4890                                      _("Length:"), _("Ideal length for connectors when layout is applied"),
4891                                      "tools.connector", "length", 100,
4892                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
4893                                      10, 1000, 10.0, 100.0,
4894                                      0, 0, 0,
4895                                      connector_length_changed, 1, 0 );
4896     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4899     // Directed edges toggle button
4900     {
4901         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
4902                                                       _("Downwards"),
4903                                                       _("Make connectors with end-markers (arrows) point downwards"),
4904                                                       "directed_graph",
4905                                                       Inkscape::ICON_SIZE_DECORATION );
4906         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4908         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
4909         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4910                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4912         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
4913     }
4915     // Avoid overlaps toggle button
4916     {
4917         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
4918                                                       _("Remove overlaps"),
4919                                                       _("Do not allow overlapping shapes"),
4920                                                       "remove_overlaps",
4921                                                       Inkscape::ICON_SIZE_DECORATION );
4922         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4924         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
4925         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4926                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4928         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
4929     }
4931     // Code to watch for changes to the connector-spacing attribute in
4932     // the XML.
4933     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4934     g_assert(repr != NULL);
4936     purge_repr_listener( holder, holder );
4938     if (repr) {
4939         g_object_set_data( holder, "repr", repr );
4940         Inkscape::GC::anchor(repr);
4941         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
4942         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
4943     }
4944 } // end of sp_connector_toolbox_prep()
4946 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* tbl)
4948     gint channels = ege_select_one_action_get_active( act );
4949     flood_channels_set_channels( channels );
4952 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject *tbl)
4954     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4957 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject *tbl)
4959     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
4962 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
4964     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
4965     SPUnit const *unit = tracker->getActiveUnit();
4968     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4971 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
4973     // FIXME: make defaults settable via Inkscape Options
4974     struct KeyValue {
4975         char const *key;
4976         double value;
4977     } const key_values[] = {
4978         {"threshold", 15},
4979         {"offset", 0.0}
4980     };
4982     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
4983         KeyValue const &kv = key_values[i];
4984         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
4985         if ( adj ) {
4986             gtk_adjustment_set_value(adj, kv.value);
4987         }
4988     }
4989     
4990     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
4991     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
4992     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
4993     ege_select_one_action_set_active( autogap_action, 0 );
4996 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4998     EgeAdjustmentAction* eact = 0;
5000     {
5001         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5003         GList* items = 0;
5004         gint count = 0;
5005         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5006         {
5007             GtkTreeIter iter;
5008             gtk_list_store_append( model, &iter );
5009             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5010             count++;
5011         }
5012         g_list_free( items );
5013         items = 0;
5014         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by:"), _(""), NULL, GTK_TREE_MODEL(model) );
5015         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5016         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5017         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5018         g_object_set_data( holder, "channels_action", act1 );
5019     }
5021     // Spacing spinbox
5022     {
5023         eact = create_adjustment_action(
5024             "ThresholdAction",
5025             _("Threshold:"),
5026             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5027             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5028             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5029             0, 0, 0,
5030             paintbucket_threshold_changed, 1, 0 );
5032         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5033     }
5035     // Create the units menu.
5036     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5037     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
5038     g_object_set_data( holder, "tracker", tracker );
5039     {
5040         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), _("") );
5041         gtk_action_group_add_action( mainActions, act );
5042     }
5044     // Offset spinbox
5045     {
5046         eact = create_adjustment_action(
5047             "OffsetAction",
5048             _("Grow/shrink by:"),
5049             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5050             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5051             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5052             0, 0, 0,
5053             paintbucket_offset_changed, 1, 2);
5054         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5056         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5057     }
5058     
5059     /* Auto Gap */
5060     {
5061         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5063         GList* items = 0;
5064         gint count = 0;
5065         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5066         {
5067             GtkTreeIter iter;
5068             gtk_list_store_append( model, &iter );
5069             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5070             count++;
5071         }
5072         g_list_free( items );
5073         items = 0;
5074         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Fill gaps:"), _(""), NULL, GTK_TREE_MODEL(model) );
5075         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5076         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5077         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5078         g_object_set_data( holder, "autogap_action", act2 );
5079     }
5081     /* Reset */
5082     {
5083         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5084                                           _("Defaults"),
5085                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5086                                           GTK_STOCK_CLEAR );
5087         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5088         gtk_action_group_add_action( mainActions, act );
5089         gtk_action_set_sensitive( act, TRUE );
5090     }
5094 /*
5095   Local Variables:
5096   mode:c++
5097   c-file-style:"stroustrup"
5098   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5099   indent-tabs-mode:nil
5100   fill-column:99
5101   End:
5102 */
5103 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :