Code

Refactoring SPColor to C++ and removing legacy CMYK implementation
[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='EditSelectAll' />"
200         "    <toolitem action='EditSelectAllInAllLayers' />"
201         "    <toolitem action='EditDeselect' />"
202         "    <separator />"
203         "    <toolitem action='ObjectRotate90CCW' />"
204         "    <toolitem action='ObjectRotate90' />"
205         "    <toolitem action='ObjectFlipHorizontally' />"
206         "    <toolitem action='ObjectFlipVertically' />"
207         "    <separator />"
208         "    <toolitem action='SelectionToBack' />"
209         "    <toolitem action='SelectionLower' />"
210         "    <toolitem action='SelectionRaise' />"
211         "    <toolitem action='SelectionToFront' />"
212         "    <separator />"
213         "    <toolitem action='XAction' />"
214         "    <toolitem action='YAction' />"
215         "    <toolitem action='WidthAction' />"
216         "    <toolitem action='LockAction' />"
217         "    <toolitem action='HeightAction' />"
218         "    <toolitem action='UnitsAction' />"
219         "    <separator />"
220         "    <toolitem action='transform_affect_label' />"
221         "    <toolitem action='transform_stroke' />"
222         "    <toolitem action='transform_corners' />"
223         "    <toolitem action='transform_gradient' />"
224         "    <toolitem action='transform_pattern' />"
225         "  </toolbar>"
227         "  <toolbar name='NodeToolbar'>"
228         "    <toolitem action='NodeInsertAction' />"
229         "    <toolitem action='NodeDeleteAction' />"
230         "    <separator />"
231         "    <toolitem action='NodeJoinAction' />"
232         "    <toolitem action='NodeJoinSegmentAction' />"
233         "    <toolitem action='NodeDeleteSegmentAction' />"
234         "    <toolitem action='NodeBreakAction' />"
235         "    <separator />"
236         "    <toolitem action='NodeCuspAction' />"
237         "    <toolitem action='NodeSmoothAction' />"
238         "    <toolitem action='NodeSymmetricAction' />"
239         "    <separator />"
240         "    <toolitem action='NodeLineAction' />"
241         "    <toolitem action='NodeCurveAction' />"
242         "    <separator />"
243         "    <toolitem action='ObjectToPath' />"
244         "    <toolitem action='StrokeToPath' />"
245         "    <separator />"
246         "    <toolitem action='NodesShowHandlesAction' />"
247         "  </toolbar>"
249         "  <toolbar name='TweakToolbar'>"
250         "    <toolitem action='TweakWidthAction' />"
251         "    <separator />"
252         "    <toolitem action='TweakForceAction' />"
253         "    <toolitem action='TweakPressureAction' />"
254         "    <separator />"
255         "    <toolitem action='TweakModeAction' />"
256         "    <separator />"
257         "    <toolitem action='TweakFidelityAction' />"
258         "    <separator />"
259         "    <toolitem action='TweakChannelsLabel' />"
260         "    <toolitem action='TweakDoH' />"
261         "    <toolitem action='TweakDoS' />"
262         "    <toolitem action='TweakDoL' />"
263         "    <toolitem action='TweakDoO' />"
264         "  </toolbar>"
266         "  <toolbar name='ZoomToolbar'>"
267         "    <toolitem action='ZoomIn' />"
268         "    <toolitem action='ZoomOut' />"
269         "    <separator />"
270         "    <toolitem action='ZoomSelection' />"
271         "    <toolitem action='ZoomDrawing' />"
272         "    <toolitem action='ZoomPage' />"
273         "    <toolitem action='ZoomPageWidth' />"
274         "    <separator />"
275         "    <toolitem action='ZoomPrev' />"
276         "    <toolitem action='ZoomNext' />"
277         "    <separator />"
278         "    <toolitem action='Zoom1:0' />"
279         "    <toolitem action='Zoom1:2' />"
280         "    <toolitem action='Zoom2:1' />"
281         "  </toolbar>"
283         "  <toolbar name='StarToolbar'>"
284         "    <separator />"
285         "    <toolitem action='StarStateAction' />"
286         "    <separator />"
287         "    <toolitem action='FlatAction' />"
288         "    <separator />"
289         "    <toolitem action='MagnitudeAction' />"
290         "    <toolitem action='SpokeAction' />"
291         "    <toolitem action='RoundednessAction' />"
292         "    <toolitem action='RandomizationAction' />"
293         "    <separator />"
294         "    <toolitem action='StarResetAction' />"
295         "  </toolbar>"
297         "  <toolbar name='RectToolbar'>"
298         "    <toolitem action='RectStateAction' />"
299         "    <toolitem action='RectWidthAction' />"
300         "    <toolitem action='RectHeightAction' />"
301         "    <toolitem action='RadiusXAction' />"
302         "    <toolitem action='RadiusYAction' />"
303         "    <toolitem action='RectUnitsAction' />"
304         "    <separator />"
305         "    <toolitem action='RectResetAction' />"
306         "  </toolbar>"
308         "  <toolbar name='3DBoxToolbar'>"
309         "    <toolitem action='3DBoxPosAngleXAction' />"
310         "    <toolitem action='3DBoxVPXAction' />"
311         "    <separator />"
312         "    <toolitem action='3DBoxPosAngleYAction' />"
313         "    <toolitem action='3DBoxVPYAction' />"
314         "    <separator />"
315         "    <toolitem action='3DBoxPosAngleZAction' />"
316         "    <toolitem action='3DBoxVPZAction' />"
317         "    <separator />"
318         "  </toolbar>"
320         "  <toolbar name='SpiralToolbar'>"
321         "    <toolitem action='SpiralStateAction' />"
322         "    <toolitem action='SpiralRevolutionAction' />"
323         "    <toolitem action='SpiralExpansionAction' />"
324         "    <toolitem action='SpiralT0Action' />"
325         "    <separator />"
326         "    <toolitem action='SpiralResetAction' />"
327         "  </toolbar>"
329         "  <toolbar name='PenToolbar'>"
330         "  </toolbar>"
332         "  <toolbar name='PencilToolbar'>"
333         "  </toolbar>"
335         "  <toolbar name='CalligraphyToolbar'>"
336         "    <separator />"
337         "    <toolitem action='CalligraphyWidthAction' />"
338         "    <toolitem action='PressureAction' />"
339         "    <toolitem action='TraceAction' />"
340         "    <toolitem action='ThinningAction' />"
341         "    <separator />"
342         "    <toolitem action='AngleAction' />"
343         "    <toolitem action='TiltAction' />"
344         "    <toolitem action='FixationAction' />"
345         "    <separator />"
346         "    <toolitem action='CapRoundingAction' />"
347         "    <separator />"
348         "    <toolitem action='TremorAction' />"
349         "    <toolitem action='WiggleAction' />"
350         "    <toolitem action='MassAction' />"
351         "    <separator />"
352         "    <toolitem action='CalligraphyResetAction' />"
353         "  </toolbar>"
355         "  <toolbar name='ArcToolbar'>"
356         "    <toolitem action='ArcStateAction' />"
357         "    <separator />"
358         "    <toolitem action='ArcStartAction' />"
359         "    <toolitem action='ArcEndAction' />"
360         "    <separator />"
361         "    <toolitem action='ArcOpenAction' />"
362         "    <separator />"
363         "    <toolitem action='ArcResetAction' />"
364         "    <separator />"
365         "  </toolbar>"
367         "  <toolbar name='PaintbucketToolbar'>"
368         "    <toolitem action='ChannelsAction' />"
369         "    <separator />"
370         "    <toolitem action='ThresholdAction' />"
371         "    <separator />"
372         "    <toolitem action='OffsetAction' />"
373         "    <toolitem action='PaintbucketUnitsAction' />"
374         "    <separator />"
375         "    <toolitem action='AutoGapAction' />"
376         "    <separator />"
377         "    <toolitem action='PaintbucketResetAction' />"
378         "  </toolbar>"
380         "  <toolbar name='DropperToolbar'>"
381         "    <toolitem action='DropperPickAlphaAction' />"
382         "    <toolitem action='DropperSetAlphaAction' />"
383         "  </toolbar>"
385         "  <toolbar name='ConnectorToolbar'>"
386         "    <toolitem action='ConnectorAvoidAction' />"
387         "    <toolitem action='ConnectorIgnoreAction' />"
388         "    <toolitem action='ConnectorSpacingAction' />"
389         "    <toolitem action='ConnectorGraphAction' />"
390         "    <toolitem action='ConnectorLengthAction' />"
391         "    <toolitem action='ConnectorDirectedAction' />"
392         "    <toolitem action='ConnectorOverlapAction' />"
393         "  </toolbar>"
395         "</ui>"
398 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
400 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
402 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
403 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
405 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
406 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
408 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
409 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
411 /* Global text entry widgets necessary for update */
412 /* GtkWidget *dropper_rgb_entry,
413           *dropper_opacity_entry ; */
414 // should be made a private member once this is converted to class
416 static void delete_connection(GObject *obj, sigc::connection *connection) {
417     connection->disconnect();
418     delete connection;
421 static void purge_repr_listener( GObject* obj, GObject* tbl )
423     (void)obj;
424     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
425     if (oldrepr) { // remove old listener
426         sp_repr_remove_listener_by_data(oldrepr, tbl);
427         Inkscape::GC::release(oldrepr);
428         oldrepr = 0;
429         g_object_set_data( tbl, "repr", NULL );
430     }
433 GtkWidget *
434 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
435                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
436                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
438     SPAction *action = verb->get_action(view);
439     if (!action) return NULL;
441     SPAction *doubleclick_action;
442     if (doubleclick_verb)
443         doubleclick_action = doubleclick_verb->get_action(view);
444     else
445         doubleclick_action = NULL;
447     /* fixme: Handle sensitive/unsensitive */
448     /* fixme: Implement sp_button_new_from_action */
449     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
450     gtk_widget_show(b);
451     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
453     return b;
456 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
457                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
459     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
462 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
463                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
465     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
469 static void trigger_sp_action( GtkAction* act, gpointer user_data )
471     SPAction* targetAction = SP_ACTION(user_data);
472     if ( targetAction ) {
473         sp_action_perform( targetAction, NULL );
474     }
477 static void sp_action_action_set_sensitive (SPAction *action, unsigned int sensitive, void *data)
479     if ( data ) {
480         GtkAction* act = GTK_ACTION(data);
481         gtk_action_set_sensitive( act, sensitive );
482     }
485 static SPActionEventVector action_event_vector = {
486     {NULL},
487     NULL,
488     NULL,
489     sp_action_action_set_sensitive,
490     NULL,
491     NULL
492 };
494 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
496     GtkAction* act = 0;
498     SPAction* targetAction = verb->get_action(view);
499     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
500     act = GTK_ACTION(inky);
501     gtk_action_set_sensitive( act, targetAction->sensitive );
503     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
505     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
506     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
508     return act;
511 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
513     Inkscape::UI::View::View *view = desktop;
514     gint verbsToUse[] = {
515         // disabled until we have icons for them:
516         //find
517         //SP_VERB_EDIT_TILE,
518         //SP_VERB_EDIT_UNTILE,
519         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
520         SP_VERB_DIALOG_DISPLAY,
521         SP_VERB_DIALOG_FILL_STROKE,
522         SP_VERB_DIALOG_NAMEDVIEW,
523         SP_VERB_DIALOG_TEXT,
524         SP_VERB_DIALOG_XML_EDITOR,
525         SP_VERB_EDIT_CLONE,
526         SP_VERB_EDIT_COPY,
527         SP_VERB_EDIT_CUT,
528         SP_VERB_EDIT_DUPLICATE,
529         SP_VERB_EDIT_PASTE,
530         SP_VERB_EDIT_REDO,
531         SP_VERB_EDIT_UNDO,
532         SP_VERB_EDIT_UNLINK_CLONE,
533         SP_VERB_FILE_EXPORT,
534         SP_VERB_FILE_IMPORT,
535         SP_VERB_FILE_NEW,
536         SP_VERB_FILE_OPEN,
537         SP_VERB_FILE_PRINT,
538         SP_VERB_FILE_SAVE,
539         SP_VERB_OBJECT_TO_CURVE,
540         SP_VERB_SELECTION_GROUP,
541         SP_VERB_SELECTION_OUTLINE,
542         SP_VERB_SELECTION_UNGROUP,
543         SP_VERB_ZOOM_1_1,
544         SP_VERB_ZOOM_1_2,
545         SP_VERB_ZOOM_2_1,
546         SP_VERB_ZOOM_DRAWING,
547         SP_VERB_ZOOM_IN,
548         SP_VERB_ZOOM_NEXT,
549         SP_VERB_ZOOM_OUT,
550         SP_VERB_ZOOM_PAGE,
551         SP_VERB_ZOOM_PAGE_WIDTH,
552         SP_VERB_ZOOM_PREV,
553         SP_VERB_ZOOM_SELECTION,
554     };
556     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
557     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
559     static std::map<SPDesktop*, GtkActionGroup*> groups;
560     GtkActionGroup* mainActions = 0;
561     if ( groups.find(desktop) != groups.end() ) {
562         mainActions = groups[desktop];
563     }
565     if ( !mainActions ) {
566         mainActions = gtk_action_group_new("main");
567         groups[desktop] = mainActions;
568     }
570     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
571         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
572         if ( verb ) {
573             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
574                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
575                 gtk_action_group_add_action( mainActions, act );
576             }
577         }
578     }
580     return mainActions;
584 GtkWidget *
585 sp_tool_toolbox_new()
587     GtkTooltips *tt = gtk_tooltips_new();
588     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
590     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
591     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
593     gtk_widget_set_sensitive(tb, FALSE);
595     GtkWidget *hb = gtk_handle_box_new();
596     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
597     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
598     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
600     gtk_container_add(GTK_CONTAINER(hb), tb);
601     gtk_widget_show(GTK_WIDGET(tb));
603     sigc::connection* conn = new sigc::connection;
604     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
606     return hb;
609 static void
610 aux_toolbox_attached(GtkHandleBox *toolbox, GtkWidget *child)
612     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
613     gtk_widget_queue_resize(child);
616 static void
617 aux_toolbox_detached(GtkHandleBox *toolbox, GtkWidget *child)
619     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
620     gtk_widget_queue_resize(child);
623 GtkWidget *
624 sp_aux_toolbox_new()
626     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
628     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
629     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
630     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
631     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
632     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
634     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
635     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
637     gtk_widget_set_sensitive(tb, FALSE);
639     GtkWidget *hb = gtk_handle_box_new();
640     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
641     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
642     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
644     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
645     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
647     gtk_container_add(GTK_CONTAINER(hb), tb);
648     gtk_widget_show(GTK_WIDGET(tb));
650     sigc::connection* conn = new sigc::connection;
651     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
653     return hb;
656 //####################################
657 //# Commands Bar
658 //####################################
660 GtkWidget *
661 sp_commands_toolbox_new()
663     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
665     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
666     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
667     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
668     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
669     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
671     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
672     gtk_widget_set_sensitive(tb, FALSE);
674     GtkWidget *hb = gtk_handle_box_new();
675     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
676     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
677     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
679     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
680     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
682     gtk_container_add(GTK_CONTAINER(hb), tb);
683     gtk_widget_show(GTK_WIDGET(tb));
685     sigc::connection* conn = new sigc::connection;
686     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
688     return hb;
692 //####################################
693 //# node editing callbacks
694 //####################################
696 /**
697  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
698  */
699 static ShapeEditor *get_current_shape_editor()
701     if (!SP_ACTIVE_DESKTOP) {
702         return NULL;
703     }
705     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
707     if (!SP_IS_NODE_CONTEXT(event_context)) {
708         return NULL;
709     }
711     return SP_NODE_CONTEXT(event_context)->shape_editor;
715 void
716 sp_node_path_edit_add(void)
718     ShapeEditor *shape_editor = get_current_shape_editor();
719     if (shape_editor) shape_editor->add_node();
722 void
723 sp_node_path_edit_delete(void)
725     ShapeEditor *shape_editor = get_current_shape_editor();
726     if (shape_editor) shape_editor->delete_nodes();
729 void
730 sp_node_path_edit_delete_segment(void)
732     ShapeEditor *shape_editor = get_current_shape_editor();
733     if (shape_editor) shape_editor->delete_segment();
736 void
737 sp_node_path_edit_break(void)
739     ShapeEditor *shape_editor = get_current_shape_editor();
740     if (shape_editor) shape_editor->break_at_nodes();
743 void
744 sp_node_path_edit_join(void)
746     ShapeEditor *shape_editor = get_current_shape_editor();
747     if (shape_editor) shape_editor->join_nodes();
750 void
751 sp_node_path_edit_join_segment(void)
753     ShapeEditor *shape_editor = get_current_shape_editor();
754     if (shape_editor) shape_editor->join_segments();
757 void
758 sp_node_path_edit_toline(void)
760     ShapeEditor *shape_editor = get_current_shape_editor();
761     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
764 void
765 sp_node_path_edit_tocurve(void)
767     ShapeEditor *shape_editor = get_current_shape_editor();
768     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
771 void
772 sp_node_path_edit_cusp(void)
774     ShapeEditor *shape_editor = get_current_shape_editor();
775     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
778 void
779 sp_node_path_edit_smooth(void)
781     ShapeEditor *shape_editor = get_current_shape_editor();
782     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
785 void
786 sp_node_path_edit_symmetrical(void)
788     ShapeEditor *shape_editor = get_current_shape_editor();
789     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
792 static void toggle_show_handles (GtkToggleAction *act, gpointer data) {
793     bool show = gtk_toggle_action_get_active( act );
794     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
795     ShapeEditor *shape_editor = get_current_shape_editor();
796     if (shape_editor) shape_editor->show_handles(show);
799 //################################
800 //##    Node Editing Toolbox    ##
801 //################################
803 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
805     {
806         InkAction* inky = ink_action_new( "NodeInsertAction",
807                                           _("Insert"),
808                                           _("Insert new nodes into selected segments"),
809                                           "node_insert",
810                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
811         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
812         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
813     }
815     {
816         InkAction* inky = ink_action_new( "NodeDeleteAction",
817                                           _("Delete"),
818                                           _("Delete selected nodes"),
819                                           "node_delete",
820                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
821         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
822         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
823     }
825     {
826         InkAction* inky = ink_action_new( "NodeJoinAction",
827                                           _("Join"),
828                                           _("Join selected endnodes"),
829                                           "node_join",
830                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
831         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
832         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
833     }
835     {
836         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
837                                           _("Join Segment"),
838                                           _("Join selected endnodes with a new segment"),
839                                           "node_join_segment",
840                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
841         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
842         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
843     }
845     {
846         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
847                                           _("Delete Segment"),
848                                           _("Split path between two non-endpoint nodes"),
849                                           "node_delete_segment",
850                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
851         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
852         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
853     }
855     {
856         InkAction* inky = ink_action_new( "NodeBreakAction",
857                                           _("Node Break"),
858                                           _("Break path at selected nodes"),
859                                           "node_break",
860                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
861         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
862         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
863     }
865     {
866         InkAction* inky = ink_action_new( "NodeCuspAction",
867                                           _("Node Cusp"),
868                                           _("Make selected nodes corner"),
869                                           "node_cusp",
870                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
871         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
872         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
873     }
875     {
876         InkAction* inky = ink_action_new( "NodeSmoothAction",
877                                           _("Node Smooth"),
878                                           _("Make selected nodes smooth"),
879                                           "node_smooth",
880                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
881         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
882         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
883     }
885     {
886         InkAction* inky = ink_action_new( "NodeSymmetricAction",
887                                           _("Node Symmetric"),
888                                           _("Make selected nodes symmetric"),
889                                           "node_symmetric",
890                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
891         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
892         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
893     }
895     {
896         InkAction* inky = ink_action_new( "NodeLineAction",
897                                           _("Node Line"),
898                                           _("Make selected segments lines"),
899                                           "node_line",
900                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
901         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
902         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
903     }
905     {
906         InkAction* inky = ink_action_new( "NodeCurveAction",
907                                           _("Node Curve"),
908                                           _("Make selected segments curves"),
909                                           "node_curve",
910                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
911         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
912         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
913     }
915     {
916         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
917                                                       _("Show Handles"),
918                                                       _("Show the Bezier handles of selected nodes"),
919                                                       "nodes_show_handles",
920                                                       Inkscape::ICON_SIZE_DECORATION );
921         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
922         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
923         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
924     }
926 } // end of sp_node_toolbox_prep()
929 //########################
930 //##    Zoom Toolbox    ##
931 //########################
933 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
935     // no custom GtkAction setup needed
936 } // end of sp_zoom_toolbox_prep()
938 void
939 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
941     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")));
945 void
946 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
948     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")));
951 void
952 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
954     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")));
957 static void
958 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
960     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
961     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
963     if (old_desktop) {
964         GList *children, *iter;
966         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
967         for ( iter = children ; iter ; iter = iter->next ) {
968             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
969         }
970         g_list_free(children);
971     }
973     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
975     if (desktop) {
976         gtk_widget_set_sensitive(toolbox, TRUE);
977         setup_func(toolbox, desktop);
978         update_func(desktop, desktop->event_context, toolbox);
979         *conn = desktop->connectEventContextChanged
980             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
981     } else {
982         gtk_widget_set_sensitive(toolbox, FALSE);
983     }
985 } // end of toolbox_set_desktop()
988 static void
989 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
991     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
992     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
993     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
994         // "toolbox.tools" was not set. Fallback to older value
995         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
997         // Copy the setting forwards
998         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
999     }
1000     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1002     for (int i = 0 ; tools[i].type_name ; i++ ) {
1003         GtkWidget *button =
1004             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1005                                                               SP_BUTTON_TYPE_TOGGLE,
1006                                                               Inkscape::Verb::get(tools[i].verb),
1007                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1008                                                               desktop,
1009                                                               tooltips );
1011         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1012                            (gpointer)button );
1013     }
1017 static void
1018 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
1020     gchar const *const tname = ( eventcontext
1021                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1022                                  : NULL );
1023     for (int i = 0 ; tools[i].type_name ; i++ ) {
1024         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1025         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1026     }
1029 static void
1030 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1032     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1033     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1034     GtkUIManager* mgr = gtk_ui_manager_new();
1035     GError* errVal = 0;
1036     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1037     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1039     std::map<std::string, GtkWidget*> dataHolders;
1041     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1042         if ( aux_toolboxes[i].prep_func ) {
1043             // converted to GtkActions and UIManager
1045             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1046             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1047             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1048             dataHolders[aux_toolboxes[i].type_name] = kludge;
1049             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1050         } else {
1052             GtkWidget *sub_toolbox = 0;
1053             if (aux_toolboxes[i].create_func == NULL)
1054                 sub_toolbox = sp_empty_toolbox_new(desktop);
1055             else {
1056                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1057             }
1059             gtk_size_group_add_widget( grouper, sub_toolbox );
1061             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1062             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1064         }
1065     }
1067     // Second pass to create toolbars *after* all GtkActions are created
1068     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1069         if ( aux_toolboxes[i].prep_func ) {
1070             // converted to GtkActions and UIManager
1072             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1074             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1075             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1077             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1078             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1079             g_free( tmp );
1080             tmp = 0;
1082             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1083             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1084             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1085                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1086             }
1087             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1090             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1092             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1093                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1094                 swatch->setDesktop( desktop );
1095                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1096                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1097                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1098                 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 );
1099             }
1101             gtk_widget_show_all( holder );
1102             sp_set_font_size_smaller( holder );
1104             gtk_size_group_add_widget( grouper, holder );
1106             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1107             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1108         }
1109     }
1111     g_object_unref( G_OBJECT(grouper) );
1114 static void
1115 update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1117     gchar const *tname = ( eventcontext
1118                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1119                            : NULL );
1120     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1121         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1122         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1123             gtk_widget_show_all(sub_toolbox);
1124             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1125         } else {
1126             gtk_widget_hide(sub_toolbox);
1127         }
1128     }
1131 static void
1132 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1134     gchar const * descr =
1135         "<ui>"
1136         "  <toolbar name='CommandsToolbar'>"
1137         "    <toolitem action='FileNew' />"
1138         "    <toolitem action='FileOpen' />"
1139         "    <toolitem action='FileSave' />"
1140         "    <toolitem action='FilePrint' />"
1141         "    <separator />"
1142         "    <toolitem action='FileImport' />"
1143         "    <toolitem action='FileExport' />"
1144         "    <separator />"
1145         "    <toolitem action='EditUndo' />"
1146         "    <toolitem action='EditRedo' />"
1147         "    <separator />"
1148         "    <toolitem action='EditCopy' />"
1149         "    <toolitem action='EditCut' />"
1150         "    <toolitem action='EditPaste' />"
1151         "    <separator />"
1152         "    <toolitem action='ZoomSelection' />"
1153         "    <toolitem action='ZoomDrawing' />"
1154         "    <toolitem action='ZoomPage' />"
1155         "    <separator />"
1156         "    <toolitem action='EditDuplicate' />"
1157         "    <toolitem action='EditClone' />"
1158         "    <toolitem action='EditUnlinkClone' />"
1159         "    <separator />"
1160         "    <toolitem action='SelectionGroup' />"
1161         "    <toolitem action='SelectionUnGroup' />"
1162         "    <separator />"
1163         "    <toolitem action='DialogFillStroke' />"
1164         "    <toolitem action='DialogText' />"
1165         "    <toolitem action='DialogXMLEditor' />"
1166         "    <toolitem action='DialogAlignDistribute' />"
1167         "    <separator />"
1168         "    <toolitem action='DialogPreferences' />"
1169         "    <toolitem action='DialogDocumentProperties' />"
1170         "  </toolbar>"
1171         "</ui>";
1172     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1175     GtkUIManager* mgr = gtk_ui_manager_new();
1176     GError* errVal = 0;
1178     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1179     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1181     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1182     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1183         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1184     }
1185     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1186     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1187     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1190     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1193 static void
1194 update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1198 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1200     gtk_widget_show(toolbox_toplevel);
1201     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1203     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1204     if (!shown_toolbox) {
1205         return;
1206     }
1207     gtk_widget_show(toolbox);
1209     // need to show the spacer, or the padding will be off
1210     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1211     gtk_widget_show(spacer);
1213     gtk_widget_show_all(shown_toolbox);
1216 void
1217 aux_toolbox_space(GtkWidget *tb, gint space)
1219     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1222 static GtkWidget *
1223 sp_empty_toolbox_new(SPDesktop *desktop)
1225     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1226     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1227     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1229     gtk_widget_show_all(tbl);
1230     sp_set_font_size_smaller (tbl);
1232     return tbl;
1235 // helper UI functions
1237 GtkWidget *
1238 sp_tb_spinbutton(
1239     gchar *label, gchar const *tooltip,
1240     gchar const *path, gchar const *data, gdouble def,
1241     GtkWidget *us,
1242     GtkWidget *tbl,
1243     gboolean altx, gchar const *altx_mark,
1244     gdouble lower, gdouble upper, gdouble step, gdouble page,
1245     void (*callback)(GtkAdjustment *, GtkWidget *),
1246     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1248     GtkTooltips *tt = gtk_tooltips_new();
1250     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1252     GtkWidget *l = gtk_label_new(label);
1253     gtk_widget_show(l);
1254     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1255     gtk_container_add(GTK_CONTAINER(hb), l);
1257     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1258                                       lower, upper, step, page, page);
1259     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1260     if (us)
1261         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1263     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1264     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1265     if (altx)
1266         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1267     gtk_widget_set_size_request(sb,
1268                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1269                                 AUX_SPINBUTTON_HEIGHT);
1270     gtk_widget_show(sb);
1271     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1272     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1273     gtk_container_add(GTK_CONTAINER(hb), sb);
1274     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1276     return hb;
1279 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1280                                                        gchar const *label, gchar const *tooltip,
1281                                                        gchar const *path, gchar const *data, gdouble def,
1282                                                        GtkWidget *focusTarget,
1283                                                        GtkWidget *us,
1284                                                        GObject *dataKludge,
1285                                                        gboolean altx, gchar const *altx_mark,
1286                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1287                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1288                                                        void (*callback)(GtkAdjustment *, GObject *),
1289                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1291     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
1292                                                              lower, upper, step, page, page ) );
1293     if (us) {
1294         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1295     }
1297     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1299     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1301     if ( (descrCount > 0) && descrLabels && descrValues ) {
1302         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1303     }
1305     if ( focusTarget ) {
1306         ege_adjustment_action_set_focuswidget( act, focusTarget );
1307     }
1309     if ( altx && altx_mark ) {
1310         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1311     }
1313     if ( dataKludge ) {
1314         g_object_set_data( dataKludge, data, adj );
1315     }
1317     // Using a cast just to make sure we pass in the right kind of function pointer
1318     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1320     return act;
1324 #define MODE_LABEL_WIDTH 70
1326 //########################
1327 //##       Star         ##
1328 //########################
1330 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1332     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1334     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1335         // do not remember prefs if this call is initiated by an undo change, because undoing object
1336         // creation sets bogus values to its attributes before it is deleted
1337         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1338     }
1340     // quit if run by the attr_changed listener
1341     if (g_object_get_data( dataKludge, "freeze" )) {
1342         return;
1343     }
1345     // in turn, prevent listener from responding
1346     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1348     bool modmade = false;
1350     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1351     GSList const *items = selection->itemList();
1352     for (; items != NULL; items = items->next) {
1353         if (SP_IS_STAR((SPItem *) items->data)) {
1354             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1355             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1356             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1357                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1358                                     + M_PI / (gint)adj->value));
1359             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1360             modmade = true;
1361         }
1362     }
1363     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1364                                    _("Star: Change number of corners"));
1366     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1369 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1371     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1373     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1374         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1375     }
1377     // quit if run by the attr_changed listener
1378     if (g_object_get_data( dataKludge, "freeze" )) {
1379         return;
1380     }
1382     // in turn, prevent listener from responding
1383     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1385     bool modmade = false;
1386     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1387     GSList const *items = selection->itemList();
1388     for (; items != NULL; items = items->next) {
1389         if (SP_IS_STAR((SPItem *) items->data)) {
1390             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1392             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1393             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1394             if (r2 < r1) {
1395                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1396             } else {
1397                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1398             }
1400             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1401             modmade = true;
1402         }
1403     }
1405     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1406                                    _("Star: Change spoke ratio"));
1408     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1411 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1413     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1414     bool flat = ege_select_one_action_get_active( act ) == 0;
1416     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1417         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1418                                     flat ? "true" : "false" );
1419     }
1421     // quit if run by the attr_changed listener
1422     if (g_object_get_data( dataKludge, "freeze" )) {
1423         return;
1424     }
1426     // in turn, prevent listener from responding
1427     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1429     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1430     GSList const *items = selection->itemList();
1431     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1432     bool modmade = false;
1434     if ( prop_action ) {
1435         gtk_action_set_sensitive( prop_action, !flat );
1436     }
1438     for (; items != NULL; items = items->next) {
1439         if (SP_IS_STAR((SPItem *) items->data)) {
1440             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1441             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1442             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1443             modmade = true;
1444         }
1445     }
1447     if (modmade) {
1448         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1449                          flat ? _("Make polygon") : _("Make star"));
1450     }
1452     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1455 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1457     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1459     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1460         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1461     }
1463     // quit if run by the attr_changed listener
1464     if (g_object_get_data( dataKludge, "freeze" )) {
1465         return;
1466     }
1468     // in turn, prevent listener from responding
1469     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1471     bool modmade = false;
1473     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1474     GSList const *items = selection->itemList();
1475     for (; items != NULL; items = items->next) {
1476         if (SP_IS_STAR((SPItem *) items->data)) {
1477             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1478             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1479             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1480             modmade = true;
1481         }
1482     }
1483     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1484                                    _("Star: Change rounding"));
1486     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1489 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1491     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1493     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1494         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1495     }
1497     // quit if run by the attr_changed listener
1498     if (g_object_get_data( dataKludge, "freeze" )) {
1499         return;
1500     }
1502     // in turn, prevent listener from responding
1503     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1505     bool modmade = false;
1507     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1508     GSList const *items = selection->itemList();
1509     for (; items != NULL; items = items->next) {
1510         if (SP_IS_STAR((SPItem *) items->data)) {
1511             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1512             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1513             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1514             modmade = true;
1515         }
1516     }
1517     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1518                                    _("Star: Change randomization"));
1520     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1524 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1525                                        gchar const *old_value, gchar const *new_value,
1526                                        bool is_interactive, gpointer data)
1528     GtkWidget *tbl = GTK_WIDGET(data);
1530     // quit if run by the _changed callbacks
1531     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1532         return;
1533     }
1535     // in turn, prevent callbacks from responding
1536     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1538     GtkAdjustment *adj = 0;
1540     if (!strcmp(name, "inkscape:randomized")) {
1541         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1542         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1543     } else if (!strcmp(name, "inkscape:rounded")) {
1544         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1545         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1546     } else if (!strcmp(name, "inkscape:flatsided")) {
1547         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1548         char const *flatsides = repr->attribute("inkscape:flatsided");
1549         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1550         if ( flatsides && !strcmp(flatsides,"false") ) {
1551             ege_select_one_action_set_active( flat_action, 1 );
1552             gtk_action_set_sensitive( prop_action, TRUE );
1553         } else {
1554             ege_select_one_action_set_active( flat_action, 0 );
1555             gtk_action_set_sensitive( prop_action, FALSE );
1556         }
1557     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1558         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1559         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1560         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1561         if (r2 < r1) {
1562             gtk_adjustment_set_value(adj, r2/r1);
1563         } else {
1564             gtk_adjustment_set_value(adj, r1/r2);
1565         }
1566     } else if (!strcmp(name, "sodipodi:sides")) {
1567         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1568         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1569     }
1571     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1575 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1577     NULL, /* child_added */
1578     NULL, /* child_removed */
1579     star_tb_event_attr_changed,
1580     NULL, /* content_changed */
1581     NULL  /* order_changed */
1582 };
1585 /**
1586  *  \param selection Should not be NULL.
1587  */
1588 static void
1589 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1591     int n_selected = 0;
1592     Inkscape::XML::Node *repr = NULL;
1594     purge_repr_listener( tbl, tbl );
1596     for (GSList const *items = selection->itemList();
1597          items != NULL;
1598          items = items->next)
1599     {
1600         if (SP_IS_STAR((SPItem *) items->data)) {
1601             n_selected++;
1602             repr = SP_OBJECT_REPR((SPItem *) items->data);
1603         }
1604     }
1606     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1608     if (n_selected == 0) {
1609         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1610     } else if (n_selected == 1) {
1611         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1613         if (repr) {
1614             g_object_set_data( tbl, "repr", repr );
1615             Inkscape::GC::anchor(repr);
1616             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1617             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1618         }
1619     } else {
1620         // FIXME: implement averaging of all parameters for multiple selected stars
1621         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1622         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1623     }
1627 static void sp_stb_defaults( GtkWidget *widget, GObject *dataKludge )
1629     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1630     // callbacks to lump all the changes for all selected objects in one undo step
1632     GtkAdjustment *adj = 0;
1634     // fixme: make settable in prefs!
1635     gint mag = 5;
1636     gdouble prop = 0.5;
1637     gboolean flat = FALSE;
1638     gdouble randomized = 0;
1639     gdouble rounded = 0;
1641     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1642     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1644     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1645     gtk_action_set_sensitive( sb2, !flat );
1647     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1648     gtk_adjustment_set_value(adj, mag);
1649     gtk_adjustment_value_changed(adj);
1651     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1652     gtk_adjustment_set_value(adj, prop);
1653     gtk_adjustment_value_changed(adj);
1655     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1656     gtk_adjustment_set_value(adj, rounded);
1657     gtk_adjustment_value_changed(adj);
1659     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1660     gtk_adjustment_set_value(adj, randomized);
1661     gtk_adjustment_value_changed(adj);
1665 void
1666 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1668     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1669     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1670     GtkWidget *l = gtk_label_new(NULL);
1671     gtk_label_set_markup(GTK_LABEL(l), title);
1672     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1673     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1674     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1678 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1680     {
1681         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1682         ege_output_action_set_use_markup( act, TRUE );
1683         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1684         g_object_set_data( holder, "mode_action", act );
1685     }
1687     {
1688         //EgeAdjustmentAction* calligraphy_angle = 0;
1689         EgeAdjustmentAction* eact = 0;
1690         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1691         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1693         /* Flatsided checkbox */
1694         {
1695             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1697             GtkTreeIter iter;
1698             gtk_list_store_append( model, &iter );
1699             gtk_list_store_set( model, &iter,
1700                                 0, _("Polygon"),
1701                                 1, _("Regular polygon (with one handle) instead of a star"),
1702                                 2, "star_flat",
1703                                 -1 );
1705             gtk_list_store_append( model, &iter );
1706             gtk_list_store_set( model, &iter,
1707                                 0, _("Star"),
1708                                 1, _("Star instead of a regular polygon (with one handle)"),
1709                                 2, "star_angled",
1710                                 -1 );
1712             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1713             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1714             g_object_set_data( holder, "flat_action", act );
1716             ege_select_one_action_set_appearance( act, "full" );
1717             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1718             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1719             ege_select_one_action_set_icon_column( act, 2 );
1720             ege_select_one_action_set_tooltip_column( act, 1  );
1722             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1723             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1724         }
1726         /* Magnitude */
1727         {
1728         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1729         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1730         eact = create_adjustment_action( "MagnitudeAction",
1731                                          _("Corners:"), _("Number of corners of a polygon or star"),
1732                                          "tools.shapes.star", "magnitude", 3,
1733                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1734                                          3, 1024, 1, 5,
1735                                          labels, values, G_N_ELEMENTS(labels),
1736                                          sp_stb_magnitude_value_changed,
1737                                          1.0, 0 );
1738         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1739         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1740         }
1742         /* Spoke ratio */
1743         {
1744         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1745         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1746         eact = create_adjustment_action( "SpokeAction",
1747                                          _("Spoke ratio:"),
1748                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1749                                          // Base radius is the same for the closest handle.
1750                                          _("Base radius to tip radius ratio"),
1751                                          "tools.shapes.star", "proportion", 0.5,
1752                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1753                                          0.01, 1.0, 0.01, 0.1,
1754                                          labels, values, G_N_ELEMENTS(labels),
1755                                          sp_stb_proportion_value_changed );
1756         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1757         g_object_set_data( holder, "prop_action", eact );
1758         }
1760         if ( !isFlatSided ) {
1761             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1762         } else {
1763             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1764         }
1766         /* Roundedness */
1767         {
1768         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1769         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1770         eact = create_adjustment_action( "RoundednessAction",
1771                                          _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1772                                          "tools.shapes.star", "rounded", 0.0,
1773                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1774                                          -10.0, 10.0, 0.01, 0.1,
1775                                          labels, values, G_N_ELEMENTS(labels),
1776                                          sp_stb_rounded_value_changed );
1777         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1778         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1779         }
1781         /* Randomization */
1782         {
1783         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1784         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1785         eact = create_adjustment_action( "RandomizationAction",
1786                                          _("Randomized:"), _("Scatter randomly the corners and angles"),
1787                                          "tools.shapes.star", "randomized", 0.0,
1788                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1789                                          -10.0, 10.0, 0.001, 0.01,
1790                                          labels, values, G_N_ELEMENTS(labels),
1791                                          sp_stb_randomized_value_changed, 0.1, 3 );
1792         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1793         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1794         }
1795     }
1797     {
1798         /* Reset */
1799         {
1800             GtkAction* act = gtk_action_new( "StarResetAction",
1801                                              _("Defaults"),
1802                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1803                                              GTK_STOCK_CLEAR );
1804             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1805             gtk_action_group_add_action( mainActions, act );
1806             gtk_action_set_sensitive( act, TRUE );
1807         }
1808     }
1810     sigc::connection *connection = new sigc::connection(
1811         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1812         );
1813     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1814     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1818 //########################
1819 //##       Rect         ##
1820 //########################
1822 static void sp_rtb_sensitivize( GObject *tbl )
1824     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1825     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1826     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1828     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1829         gtk_action_set_sensitive( not_rounded, FALSE );
1830     } else {
1831         gtk_action_set_sensitive( not_rounded, TRUE );
1832     }
1836 static void
1837 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1838                           void (*setter)(SPRect *, gdouble))
1840     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1842     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1843     SPUnit const *unit = tracker->getActiveUnit();
1845     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1846         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1847     }
1849     // quit if run by the attr_changed listener
1850     if (g_object_get_data( tbl, "freeze" )) {
1851         return;
1852     }
1854     // in turn, prevent listener from responding
1855     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1857     bool modmade = false;
1858     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1859     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1860         if (SP_IS_RECT(items->data)) {
1861             if (adj->value != 0) {
1862                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
1863             } else {
1864                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
1865             }
1866             modmade = true;
1867         }
1868     }
1870     sp_rtb_sensitivize( tbl );
1872     if (modmade) {
1873         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
1874                                    _("Change rectangle"));
1875     }
1877     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1880 static void
1881 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
1883     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
1886 static void
1887 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
1889     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
1892 static void
1893 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
1895     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
1898 static void
1899 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
1901     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
1906 static void
1907 sp_rtb_defaults( GtkWidget *widget, GObject *obj)
1909     GtkAdjustment *adj = 0;
1911     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
1912     gtk_adjustment_set_value(adj, 0.0);
1913     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
1914     gtk_adjustment_value_changed(adj);
1916     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
1917     gtk_adjustment_set_value(adj, 0.0);
1918     gtk_adjustment_value_changed(adj);
1920     sp_rtb_sensitivize( obj );
1923 static void rect_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1924                                        gchar const *old_value, gchar const *new_value,
1925                                        bool is_interactive, gpointer data)
1927     GObject *tbl = G_OBJECT(data);
1929     // quit if run by the _changed callbacks
1930     if (g_object_get_data( tbl, "freeze" )) {
1931         return;
1932     }
1934     // in turn, prevent callbacks from responding
1935     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
1937     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1938     SPUnit const *unit = tracker->getActiveUnit();
1940     gpointer item = g_object_get_data( tbl, "item" );
1941     if (item && SP_IS_RECT(item)) {
1942         {
1943             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
1944             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
1945             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
1946         }
1948         {
1949             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
1950             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
1951             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
1952         }
1954         {
1955             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
1956             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
1957             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
1958         }
1960         {
1961             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
1962             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
1963             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
1964         }
1965     }
1967     sp_rtb_sensitivize( tbl );
1969     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1973 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
1974     NULL, /* child_added */
1975     NULL, /* child_removed */
1976     rect_tb_event_attr_changed,
1977     NULL, /* content_changed */
1978     NULL  /* order_changed */
1979 };
1981 /**
1982  *  \param selection should not be NULL.
1983  */
1984 static void
1985 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1987     int n_selected = 0;
1988     Inkscape::XML::Node *repr = NULL;
1989     SPItem *item = NULL;
1991     if ( g_object_get_data( tbl, "repr" ) ) {
1992         g_object_set_data( tbl, "item", NULL );
1993     }
1994     purge_repr_listener( tbl, tbl );
1996     for (GSList const *items = selection->itemList();
1997          items != NULL;
1998          items = items->next) {
1999         if (SP_IS_RECT((SPItem *) items->data)) {
2000             n_selected++;
2001             item = (SPItem *) items->data;
2002             repr = SP_OBJECT_REPR(item);
2003         }
2004     }
2006     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2008     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2010     if (n_selected == 0) {
2011         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2013         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2014         gtk_action_set_sensitive(w, FALSE);
2015         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2016         gtk_action_set_sensitive(h, FALSE);
2018     } else if (n_selected == 1) {
2019         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2020         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2022         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2023         gtk_action_set_sensitive(w, TRUE);
2024         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2025         gtk_action_set_sensitive(h, TRUE);
2027         if (repr) {
2028             g_object_set_data( tbl, "repr", repr );
2029             g_object_set_data( tbl, "item", item );
2030             Inkscape::GC::anchor(repr);
2031             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2032             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2033         }
2034     } else {
2035         // FIXME: implement averaging of all parameters for multiple selected
2036         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2037         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2038         sp_rtb_sensitivize( tbl );
2039     }
2043 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2045     EgeAdjustmentAction* eact = 0;
2047     {
2048         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2049         ege_output_action_set_use_markup( act, TRUE );
2050         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2051         g_object_set_data( holder, "mode_action", act );
2052     }
2054     // rx/ry units menu: create
2055     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2056     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2057     // fixme: add % meaning per cent of the width/height
2058     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2059     g_object_set_data( holder, "tracker", tracker );
2061     /* W */
2062     {
2063         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2064         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2065         eact = create_adjustment_action( "RectWidthAction",
2066                                          _("W:"), _("Width of rectangle"),
2067                                          "tools.shapes.rect", "width", 0,
2068                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2069                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2070                                          labels, values, G_N_ELEMENTS(labels),
2071                                          sp_rtb_width_value_changed );
2072         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2073         g_object_set_data( holder, "width_action", eact );
2074         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2075         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2076     }
2078     /* H */
2079     {
2080         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2081         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2082         eact = create_adjustment_action( "RectHeightAction",
2083                                          _("H:"), _("Height of rectangle"),
2084                                          "tools.shapes.rect", "height", 0,
2085                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2086                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2087                                          labels, values, G_N_ELEMENTS(labels),
2088                                          sp_rtb_height_value_changed );
2089         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2090         g_object_set_data( holder, "height_action", eact );
2091         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2092         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2093     }
2095     /* rx */
2096     {
2097         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2098         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2099         eact = create_adjustment_action( "RadiusXAction",
2100                                          _("Rx:"), _("Horizontal radius of rounded corners"),
2101                                          "tools.shapes.rect", "rx", 0,
2102                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2103                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2104                                          labels, values, G_N_ELEMENTS(labels),
2105                                          sp_rtb_rx_value_changed);
2106         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2107         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2108     }
2110     /* ry */
2111     {
2112         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2113         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2114         eact = create_adjustment_action( "RadiusYAction",
2115                                          _("Ry:"), _("Vertical radius of rounded corners"),
2116                                          "tools.shapes.rect", "ry", 0,
2117                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2118                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2119                                          labels, values, G_N_ELEMENTS(labels),
2120                                          sp_rtb_ry_value_changed);
2121         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2122         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2123     }
2125     // add the units menu
2126     {
2127         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2128         gtk_action_group_add_action( mainActions, act );
2129     }
2131     /* Reset */
2132     {
2133         InkAction* inky = ink_action_new( "RectResetAction",
2134                                           _("Not rounded"),
2135                                           _("Make corners sharp"),
2136                                           "squared_corner",
2137                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2138         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2139         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2140         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2141         g_object_set_data( holder, "not_rounded", inky );
2142     }
2144     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2145     sp_rtb_sensitivize( holder );
2147     sigc::connection *connection = new sigc::connection(
2148         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2149         );
2150     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2151     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2154 //########################
2155 //##       3D Box       ##
2156 //########################
2158 static void sp_3dbox_toggle_vp_changed (GtkToggleAction *act, GObject *dataKludge, Box3D::Axis axis)
2160     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2161     SPDocument *document = sp_desktop_document (desktop);
2162     Box3D::Perspective3D *persp = document->current_perspective;
2164     g_return_if_fail (is_single_axis_direction (axis));
2165     g_return_if_fail (persp);
2167     persp->toggle_boxes (axis);
2169     gchar *str;    
2170     switch (axis) {
2171         case Box3D::X:
2172             str = g_strdup ("box3d_angle_x_action");
2173             break;
2174         case Box3D::Y:
2175             str = g_strdup ("box3d_angle_y_action");
2176             break;
2177         case Box3D::Z:
2178             str = g_strdup ("box3d_angle_z_action");
2179             break;
2180         default:
2181             return;
2182     }
2183     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2184     if (angle_action) {
2185         gtk_action_set_sensitive (angle_action, !persp->get_vanishing_point (axis)->is_finite() );
2186     }
2188     // FIXME: Given how it is realized in the other tools, this is probably not the right way to do it,
2189     //        but without the if construct, we get continuous segfaults. Needs further investigation.
2190     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2191         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
2192                          _("3D Box: Change perspective"));
2193     }
2196 static void sp_3dbox_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2198     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::X);
2201 static void sp_3dbox_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2203     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Y);
2206 static void sp_3dbox_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2208     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Z);
2211 static void sp_3dbox_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Box3D::Axis axis )
2213     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2214     Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2216     if (persp) {
2217         double angle = adj->value * M_PI/180;
2218         persp->set_infinite_direction (axis, NR::Point (cos (angle), sin (angle)));
2220         // FIXME: See comment above; without the if construct we get segfaults during undo.
2221         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2222             sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2223                              _("3D Box: Change perspective"));
2224         }
2225     }
2226     //g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
2229 static void sp_3dbox_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2231     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::X);
2234 static void sp_3dbox_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2236     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Y);
2239 static void sp_3dbox_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2241     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Z);
2244 // normalize angle so that it lies in the interval [0,360]
2245 static double sp_3dbox_normalize_angle (double a) {
2246     double angle = a + ((int) (a/360.0))*360;
2247     if (angle < 0) {
2248         angle += 360.0;
2249     }
2250     return angle;
2253 static void sp_3dbox_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2254                                            gchar const *old_value, gchar const *new_value,
2255                                            bool is_interactive, gpointer data)
2257     GtkWidget *tbl = GTK_WIDGET(data);
2259     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2260     /***
2261     // quit if run by the _changed callbacks
2262     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2263         return;
2264     }
2266     // in turn, prevent callbacks from responding
2267     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2268     ***/
2270     if (!strcmp(name, "inkscape:perspective")) {
2271         GtkAdjustment *adj = 0;
2272         double angle;
2273         SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2274         Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2276         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2277         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::X)->get_angle());
2278         gtk_adjustment_set_value(adj, angle);
2280         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2281         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Y)->get_angle());
2282         gtk_adjustment_set_value(adj, angle);
2284         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2285         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Z)->get_angle());
2286         gtk_adjustment_set_value(adj, angle);
2287     }
2290 static Inkscape::XML::NodeEventVector sp_3dbox_tb_repr_events =
2292     NULL, /* child_added */
2293     NULL, /* child_removed */
2294     sp_3dbox_tb_event_attr_changed,
2295     NULL, /* content_changed */
2296     NULL  /* order_changed */
2297 };
2299 /**
2300  *  \param selection Should not be NULL.
2301  */
2302 static void
2303 sp_3dbox_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2305     Inkscape::XML::Node *repr = NULL;
2306     purge_repr_listener(tbl, tbl);
2308     SPItem *item = selection->singleItem();
2309     if (item) {
2310         repr = SP_OBJECT_REPR(item);
2311         if (repr) {
2312             g_object_set_data(tbl, "repr", repr);
2313             Inkscape::GC::anchor(repr);
2314             sp_repr_add_listener(repr, &sp_3dbox_tb_repr_events, tbl);
2315             sp_repr_synthesize_events(repr, &sp_3dbox_tb_repr_events, tbl);
2316         }
2317     }
2320 static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2322     EgeAdjustmentAction* eact = 0;
2323     SPDocument *document = sp_desktop_document (desktop);
2324     Box3D::Perspective3D *persp = document->current_perspective;
2325     bool toggled = false;
2327     /* angle of VP in X direction */
2328     eact = create_adjustment_action("3DBoxPosAngleXAction",
2329                                     _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2330                                     "tools.shapes.3dbox", "dir_vp_x", persp->get_vanishing_point (Box3D::X)->get_angle(),
2331                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2332                                     0.0, 360.0, 1.0, 10.0,
2333                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2334                                     sp_3dbox_vpx_angle_changed,
2335                                     0.1, 1);
2336     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2337     g_object_set_data(holder, "box3d_angle_x_action", eact);
2338     if (!persp->get_vanishing_point (Box3D::X)->is_finite()) {
2339         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2340     } else {
2341         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2342     }
2344     /* toggle VP in X direction */
2345     {
2346     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2347                                                   _("Toggle VP in X direction"),
2348                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2349                                                   "toggle_vp_x",
2350                                                   Inkscape::ICON_SIZE_DECORATION);
2351     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2352     if (persp) {
2353         toggled = !persp->get_vanishing_point(Box3D::X)->is_finite();
2354     }
2355     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2356     /* we connect the signal after setting the state to avoid switching the state again */
2357     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_x_changed), holder);
2358     }
2360     /* angle of VP in Y direction */
2361     eact = create_adjustment_action("3DBoxPosAngleYAction",
2362                                     _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2363                                     "tools.shapes.3dbox", "dir_vp_y", persp->get_vanishing_point (Box3D::Y)->get_angle(),
2364                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2365                                     0.0, 360.0, 1.0, 10.0,
2366                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2367                                     sp_3dbox_vpy_angle_changed,
2368                                     0.1, 1);
2369     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2370     g_object_set_data(holder, "box3d_angle_y_action", eact);
2371     if (!persp->get_vanishing_point (Box3D::Y)->is_finite()) {
2372         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2373     } else {
2374         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2375     }
2377     /* toggle VP in Y direction */
2378     {
2379     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2380                                                  _("Toggle VP in Y direction"),
2381                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2382                                                  "toggle_vp_y",
2383                                                  Inkscape::ICON_SIZE_DECORATION);
2384     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2385     if (persp) {
2386         toggled = !persp->get_vanishing_point(Box3D::Y)->is_finite();
2387     }
2388     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2389     /* we connect the signal after setting the state to avoid switching the state again */
2390     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_y_changed), holder);
2391     }
2393     /* angle of VP in Z direction */
2394     eact = create_adjustment_action("3DBoxPosAngleZAction",
2395                                     _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2396                                     "tools.shapes.3dbox", "dir_vp_z", persp->get_vanishing_point (Box3D::Z)->get_angle(),
2397                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2398                                      0.0, 360.0, 1.0, 10.0,
2399                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2400                                     sp_3dbox_vpz_angle_changed,
2401                                     0.1, 1);
2402                                     
2403     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2404     g_object_set_data(holder, "box3d_angle_z_action", eact);
2405     if (!persp->get_vanishing_point (Box3D::Z)->is_finite()) {
2406         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2407     } else {
2408         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2409     }
2411     /* toggle VP in Z direction */
2412     {
2413     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2414                                                  _("Toggle VP in Z direction"),
2415                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2416                                                  "toggle_vp_z",
2417                                                  Inkscape::ICON_SIZE_DECORATION);
2418     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2419     if (persp) {
2420         toggled = !persp->get_vanishing_point(Box3D::Z)->is_finite();
2421     }
2422     /* we connect the signal after setting the state to avoid switching the state again */
2423     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2424     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_z_changed), holder);
2425     }
2427     sigc::connection *connection = new sigc::connection(
2428         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_3dbox_toolbox_selection_changed), (GObject *)holder))
2429        );
2430     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2431     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2434 //########################
2435 //##       Spiral       ##
2436 //########################
2438 static void
2439 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2441     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2443     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2444         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2445     }
2447     // quit if run by the attr_changed listener
2448     if (g_object_get_data( tbl, "freeze" )) {
2449         return;
2450     }
2452     // in turn, prevent listener from responding
2453     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2455     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2457     bool modmade = false;
2458     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2459          items != NULL;
2460          items = items->next)
2461     {
2462         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2463             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2464             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2465             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2466             modmade = true;
2467         }
2468     }
2470     g_free(namespaced_name);
2472     if (modmade) {
2473         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2474                                    _("Change spiral"));
2475     }
2477     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2480 static void
2481 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2483     sp_spl_tb_value_changed(adj, tbl, "revolution");
2486 static void
2487 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2489     sp_spl_tb_value_changed(adj, tbl, "expansion");
2492 static void
2493 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2495     sp_spl_tb_value_changed(adj, tbl, "t0");
2498 static void
2499 sp_spl_tb_defaults(GtkWidget *widget, GtkObject *obj)
2501     GtkWidget *tbl = GTK_WIDGET(obj);
2503     GtkAdjustment *adj;
2505     // fixme: make settable
2506     gdouble rev = 5;
2507     gdouble exp = 1.0;
2508     gdouble t0 = 0.0;
2510     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2511     gtk_adjustment_set_value(adj, rev);
2512     gtk_adjustment_value_changed(adj);
2514     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2515     gtk_adjustment_set_value(adj, exp);
2516     gtk_adjustment_value_changed(adj);
2518     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2519     gtk_adjustment_set_value(adj, t0);
2520     gtk_adjustment_value_changed(adj);
2522     spinbutton_defocus(GTK_OBJECT(tbl));
2526 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2527                                          gchar const *old_value, gchar const *new_value,
2528                                          bool is_interactive, gpointer data)
2530     GtkWidget *tbl = GTK_WIDGET(data);
2532     // quit if run by the _changed callbacks
2533     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2534         return;
2535     }
2537     // in turn, prevent callbacks from responding
2538     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2540     GtkAdjustment *adj;
2541     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2542     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2544     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2545     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2547     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2548     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2550     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2554 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2555     NULL, /* child_added */
2556     NULL, /* child_removed */
2557     spiral_tb_event_attr_changed,
2558     NULL, /* content_changed */
2559     NULL  /* order_changed */
2560 };
2562 static void
2563 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2565     int n_selected = 0;
2566     Inkscape::XML::Node *repr = NULL;
2568     purge_repr_listener( tbl, tbl );
2570     for (GSList const *items = selection->itemList();
2571          items != NULL;
2572          items = items->next)
2573     {
2574         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2575             n_selected++;
2576             repr = SP_OBJECT_REPR((SPItem *) items->data);
2577         }
2578     }
2580     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2582     if (n_selected == 0) {
2583         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2584     } else if (n_selected == 1) {
2585         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2587         if (repr) {
2588             g_object_set_data( tbl, "repr", repr );
2589             Inkscape::GC::anchor(repr);
2590             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2591             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2592         }
2593     } else {
2594         // FIXME: implement averaging of all parameters for multiple selected
2595         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2596         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2597     }
2601 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2603     EgeAdjustmentAction* eact = 0;
2605     {
2606         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2607         ege_output_action_set_use_markup( act, TRUE );
2608         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2609         g_object_set_data( holder, "mode_action", act );
2610     }
2612     /* Revolution */
2613     {
2614         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2615         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2616         eact = create_adjustment_action( "SpiralRevolutionAction",
2617                                          _("Turns:"), _("Number of revolutions"),
2618                                          "tools.shapes.spiral", "revolution", 3.0,
2619                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2620                                          0.01, 1024.0, 0.1, 1.0,
2621                                          labels, values, G_N_ELEMENTS(labels),
2622                                          sp_spl_tb_revolution_value_changed, 1, 2);
2623         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2624     }
2626     /* Expansion */
2627     {
2628         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2629         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2630         eact = create_adjustment_action( "SpiralExpansionAction",
2631                                          _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2632                                          "tools.shapes.spiral", "expansion", 1.0,
2633                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2634                                          0.0, 1000.0, 0.01, 1.0,
2635                                          labels, values, G_N_ELEMENTS(labels),
2636                                          sp_spl_tb_expansion_value_changed);
2637         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2638     }
2640     /* T0 */
2641     {
2642         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2643         gdouble values[] = {0, 0.5, 0.9};
2644         eact = create_adjustment_action( "SpiralT0Action",
2645                                          _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2646                                          "tools.shapes.spiral", "t0", 0.0,
2647                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2648                                          0.0, 0.999, 0.01, 1.0,
2649                                          labels, values, G_N_ELEMENTS(labels),
2650                                          sp_spl_tb_t0_value_changed);
2651         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2652     }
2654     /* Reset */
2655     {
2656         InkAction* inky = ink_action_new( "SpiralResetAction",
2657                                           _("Defaults"),
2658                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2659                                           GTK_STOCK_CLEAR,
2660                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2661         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2662         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2663     }
2666     sigc::connection *connection = new sigc::connection(
2667         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2668         );
2669     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2670     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2673 //########################
2674 //##     Pen/Pencil    ##
2675 //########################
2678 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2680     // Put stuff here
2683 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2685     // Put stuff here
2688 //########################
2689 //##       Tweak        ##
2690 //########################
2692 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2694     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2697 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject *tbl )
2699     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2702 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer data )
2704     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2707 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2709     int mode = ege_select_one_action_get_active( act );
2710     prefs_set_int_attribute("tools.tweak", "mode", mode);
2712     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2713     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2714     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2715     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2716     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2717     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2718     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2719         if (doh) gtk_action_set_sensitive (doh, TRUE);
2720         if (dos) gtk_action_set_sensitive (dos, TRUE);
2721         if (dol) gtk_action_set_sensitive (dol, TRUE);
2722         if (doo) gtk_action_set_sensitive (doo, TRUE);
2723         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2724         if (fid) gtk_action_set_sensitive (fid, FALSE);
2725     } else {
2726         if (doh) gtk_action_set_sensitive (doh, FALSE);
2727         if (dos) gtk_action_set_sensitive (dos, FALSE);
2728         if (dol) gtk_action_set_sensitive (dol, FALSE);
2729         if (doo) gtk_action_set_sensitive (doo, FALSE);
2730         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2731         if (fid) gtk_action_set_sensitive (fid, TRUE);
2732     }
2735 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject *tbl )
2737     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2740 static void tweak_toggle_doh (GtkToggleAction *act, gpointer data) {
2741     bool show = gtk_toggle_action_get_active( act );
2742     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2744 static void tweak_toggle_dos (GtkToggleAction *act, gpointer data) {
2745     bool show = gtk_toggle_action_get_active( act );
2746     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2748 static void tweak_toggle_dol (GtkToggleAction *act, gpointer data) {
2749     bool show = gtk_toggle_action_get_active( act );
2750     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2752 static void tweak_toggle_doo (GtkToggleAction *act, gpointer data) {
2753     bool show = gtk_toggle_action_get_active( act );
2754     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
2757 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2759     {
2760         /* Width */
2761         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2762         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2763         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2764                                                               _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2765                                                               "tools.tweak", "width", 15,
2766                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2767                                                               1, 100, 1.0, 10.0,
2768                                                               labels, values, G_N_ELEMENTS(labels),
2769                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2770         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2771         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2772     }
2775     {
2776         /* Force */
2777         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2778         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2779         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2780                                                               _("Force:"), _("The force of the tweak action"),
2781                                                               "tools.tweak", "force", 20,
2782                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2783                                                               1, 100, 1.0, 10.0,
2784                                                               labels, values, G_N_ELEMENTS(labels),
2785                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2786         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2787         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2788     }
2790     /* Mode */
2791     {
2792         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2794         GtkTreeIter iter;
2795         gtk_list_store_append( model, &iter );
2796         gtk_list_store_set( model, &iter,
2797                             0, _("Push mode"),
2798                             1, _("Push parts of paths in any direction"),
2799                             2, "tweak_push_mode",
2800                             -1 );
2802         gtk_list_store_append( model, &iter );
2803         gtk_list_store_set( model, &iter,
2804                             0, _("Shrink mode"),
2805                             1, _("Shrink (inset) parts of paths"),
2806                             2, "tweak_shrink_mode",
2807                             -1 );
2809         gtk_list_store_append( model, &iter );
2810         gtk_list_store_set( model, &iter,
2811                             0, _("Grow mode"),
2812                             1, _("Grow (outset) parts of paths"),
2813                             2, "tweak_grow_mode",
2814                             -1 );
2816         gtk_list_store_append( model, &iter );
2817         gtk_list_store_set( model, &iter,
2818                             0, _("Attract mode"),
2819                             1, _("Attract parts of paths towards cursor"),
2820                             2, "tweak_attract_mode",
2821                             -1 );
2823         gtk_list_store_append( model, &iter );
2824         gtk_list_store_set( model, &iter,
2825                             0, _("Repel mode"),
2826                             1, _("Repel parts of paths from cursor"),
2827                             2, "tweak_repel_mode",
2828                             -1 );
2830         gtk_list_store_append( model, &iter );
2831         gtk_list_store_set( model, &iter,
2832                             0, _("Roughen mode"),
2833                             1, _("Roughen parts of paths"),
2834                             2, "tweak_roughen_mode",
2835                             -1 );
2837         gtk_list_store_append( model, &iter );
2838         gtk_list_store_set( model, &iter,
2839                             0, _("Color paint mode"),
2840                             1, _("Paint the tool's color upon selected objects"),
2841                             2, "tweak_colorpaint_mode",
2842                             -1 );
2844         gtk_list_store_append( model, &iter );
2845         gtk_list_store_set( model, &iter,
2846                             0, _("Color jitter mode"),
2847                             1, _("Jitter the colors of selected objects"),
2848                             2, "tweak_colorjitter_mode",
2849                             -1 );
2851         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode:"), (""), NULL, GTK_TREE_MODEL(model) );
2852         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2853         g_object_set_data( holder, "mode_action", act );
2855         ege_select_one_action_set_appearance( act, "full" );
2856         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2857         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2858         ege_select_one_action_set_icon_column( act, 2 );
2859         ege_select_one_action_set_tooltip_column( act, 1  );
2861         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2862         ege_select_one_action_set_active( act, mode );
2863         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
2865         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
2866     }
2868     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2870     {
2871         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
2872         ege_output_action_set_use_markup( act, TRUE );
2873         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2874         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2875             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2876         g_object_set_data( holder, "tweak_channels_label", act);
2877     }
2879     {
2880         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
2881                                                       _("H"),
2882                                                       _("In color mode, act on objects' hue"),
2883                                                       NULL,
2884                                                       Inkscape::ICON_SIZE_DECORATION );
2885         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2886         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
2887         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
2888         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2889             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2890         g_object_set_data( holder, "tweak_doh", act);
2891     }
2892     {
2893         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
2894                                                       _("S"),
2895                                                       _("In color mode, act on objects' saturation"),
2896                                                       NULL,
2897                                                       Inkscape::ICON_SIZE_DECORATION );
2898         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2899         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
2900         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
2901         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2902             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2903         g_object_set_data( holder, "tweak_dos", act );
2904     }
2905     {
2906         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
2907                                                       _("L"),
2908                                                       _("In color mode, act on objects' lightness"),
2909                                                       NULL,
2910                                                       Inkscape::ICON_SIZE_DECORATION );
2911         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2912         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
2913         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
2914         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2915             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2916         g_object_set_data( holder, "tweak_dol", act );
2917     }
2918     {
2919         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
2920                                                       _("O"),
2921                                                       _("In color mode, act on objects' opacity"),
2922                                                       NULL,
2923                                                       Inkscape::ICON_SIZE_DECORATION );
2924         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2925         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
2926         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
2927         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) 
2928             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2929         g_object_set_data( holder, "tweak_doo", act );
2930     }
2932     {   /* Fidelity */
2933         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
2934         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
2935         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
2936                                                               _("Fidelity:"), _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
2937                                                               "tools.tweak", "fidelity", 50,
2938                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
2939                                                               1, 100, 1.0, 10.0,
2940                                                               labels, values, G_N_ELEMENTS(labels),
2941                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
2942         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2943         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2944         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) 
2945             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
2946         g_object_set_data( holder, "tweak_fidelity", eact );
2947     }
2950     /* Use Pressure button */
2951     {
2952         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
2953                                                       _("Pressure"),
2954                                                       _("Use the pressure of the input device to alter the force of tweak action"),
2955                                                       "use_pressure",
2956                                                       Inkscape::ICON_SIZE_DECORATION );
2957         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2958         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
2959         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
2960     }
2965 //########################
2966 //##     Calligraphy    ##
2967 //########################
2969 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
2971     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2974 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
2976     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2979 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
2981     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2984 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2986     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2989 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
2991     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
2994 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
2996     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
2999 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3001     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3004 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3006     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3009 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer data )
3011     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3014 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer data )
3016     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3019 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3021     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3023     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3026 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3028     // FIXME: make defaults settable via Inkscape Options
3029     struct KeyValue {
3030         char const *key;
3031         double value;
3032     } const key_values[] = {
3033         {"mass", 0.02},
3034         {"wiggle", 0.0},
3035         {"angle", 30.0},
3036         {"width", 15},
3037         {"thinning", 0.1},
3038         {"tremor", 0.0},
3039         {"flatness", 0.9},
3040         {"cap_rounding", 0.0}
3041     };
3043     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3044         KeyValue const &kv = key_values[i];
3045         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3046         if ( adj ) {
3047             gtk_adjustment_set_value(adj, kv.value);
3048         }
3049     }
3053 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3055     {
3056         EgeAdjustmentAction* calligraphy_angle = 0;
3058         {
3059         /* Width */
3060         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3061         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3062         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3063                                          _("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
3064                                          "tools.calligraphic", "width", 15,
3065                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3066                                          1, 100, 1.0, 10.0,
3067                                          labels, values, G_N_ELEMENTS(labels),
3068                                          sp_ddc_width_value_changed,  0.01, 0, 100 );
3069         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3070         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3071         }
3073         {
3074         /* Thinning */
3075             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3076             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3077         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3078                                          _("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3079                                          "tools.calligraphic", "thinning", 0.1,
3080                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3081                                          -1.0, 1.0, 0.01, 0.1,
3082                                          labels, values, G_N_ELEMENTS(labels),
3083                                          sp_ddc_velthin_value_changed, 0.01, 2);
3084         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3085         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3086         }
3088         {
3089         /* Angle */
3090         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3091         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3092         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3093                                          _("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3094                                          "tools.calligraphic", "angle", 30,
3095                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3096                                          -90.0, 90.0, 1.0, 10.0,
3097                                          labels, values, G_N_ELEMENTS(labels),
3098                                          sp_ddc_angle_value_changed, 1, 0 );
3099         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3100         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3101         calligraphy_angle = eact;
3102         }
3104         {
3105         /* Fixation */
3106             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3107         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3108         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3109                                          _("Fixation:"), _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3110                                          "tools.calligraphic", "flatness", 0.9,
3111                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3112                                          0.0, 1.0, 0.01, 0.1,
3113                                          labels, values, G_N_ELEMENTS(labels),
3114                                          sp_ddc_flatness_value_changed, 0.01, 2 );
3115         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3116         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3117         }
3119         {
3120         /* Cap Rounding */
3121             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3122         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3123         // TRANSLATORS: "cap" means "end" (both start and finish) here
3124         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3125                                          _("Caps:"), _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3126                                          "tools.calligraphic", "cap_rounding", 0.0,
3127                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3128                                          0.0, 5.0, 0.01, 0.1,
3129                                          labels, values, G_N_ELEMENTS(labels),
3130                                          sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3131         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3132         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3133         }
3135         {
3136         /* Tremor */
3137             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3138         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3139         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3140                                          _("Tremor:"), _("Increase to make strokes rugged and trembling"),
3141                                          "tools.calligraphic", "tremor", 0.0,
3142                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3143                                          0.0, 1.0, 0.01, 0.1,
3144                                          labels, values, G_N_ELEMENTS(labels),
3145                                          sp_ddc_tremor_value_changed, 0.01, 2 );
3147         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3148         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3149         }
3151         {
3152         /* Wiggle */
3153         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3154         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3155         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3156                                          _("Wiggle:"), _("Increase to make the pen waver and wiggle"),
3157                                          "tools.calligraphic", "wiggle", 0.0,
3158                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3159                                          0.0, 1.0, 0.01, 0.1,
3160                                          labels, values, G_N_ELEMENTS(labels),
3161                                          sp_ddc_wiggle_value_changed, 0.01, 2 );
3162         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3163         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3164         }
3166         {
3167         /* Mass */
3168             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3169         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3170         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3171                                          _("Mass:"), _("Increase to make the pen drag behind, as if slowed by inertia"),
3172                                          "tools.calligraphic", "mass", 0.02,
3173                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3174                                          0.0, 1.0, 0.01, 0.1,
3175                                          labels, values, G_N_ELEMENTS(labels),
3176                                          sp_ddc_mass_value_changed, 0.01, 2 );
3177         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3178         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3179         }
3182         /* Trace Background button */
3183         {
3184             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3185                                                           _("Trace Background"),
3186                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3187                                                           "trace_background",
3188                                                           Inkscape::ICON_SIZE_DECORATION );
3189             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3190             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3191             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3192         }
3194         /* Use Pressure button */
3195         {
3196             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3197                                                           _("Pressure"),
3198                                                           _("Use the pressure of the input device to alter the width of the pen"),
3199                                                           "use_pressure",
3200                                                           Inkscape::ICON_SIZE_DECORATION );
3201             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3202             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3203             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3204         }
3206         /* Use Tilt button */
3207         {
3208             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3209                                                           _("Tilt"),
3210                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3211                                                           "use_tilt",
3212                                                           Inkscape::ICON_SIZE_DECORATION );
3213             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3214             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3215             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3216             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3217         }
3219         /* Reset */
3220         {
3221             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3222                                              _("Defaults"),
3223                                              _("Reset all parameters to defaults"),
3224                                              GTK_STOCK_CLEAR );
3225             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3226             gtk_action_group_add_action( mainActions, act );
3227             gtk_action_set_sensitive( act, TRUE );
3228         }
3229     }
3233 //########################
3234 //##    Circle / Arc    ##
3235 //########################
3237 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3239     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3240     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3242     if (v1 == 0 && v2 == 0) {
3243         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3244             gtk_action_set_sensitive( ocb, FALSE );
3245             gtk_action_set_sensitive( make_whole, FALSE );
3246         }
3247     } else {
3248         gtk_action_set_sensitive( ocb, TRUE );
3249         gtk_action_set_sensitive( make_whole, TRUE );
3250     }
3253 static void
3254 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3256     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3258     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3259         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3260     }
3262     // quit if run by the attr_changed listener
3263     if (g_object_get_data( tbl, "freeze" )) {
3264         return;
3265     }
3267     // in turn, prevent listener from responding
3268     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3270     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3272     bool modmade = false;
3273     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3274          items != NULL;
3275          items = items->next)
3276     {
3277         SPItem *item = SP_ITEM(items->data);
3279         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3281             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3282             SPArc *arc = SP_ARC(item);
3284             if (!strcmp(value_name, "start"))
3285                 ge->start = (adj->value * M_PI)/ 180;
3286             else
3287                 ge->end = (adj->value * M_PI)/ 180;
3289             sp_genericellipse_normalize(ge);
3290             ((SPObject *)arc)->updateRepr();
3291             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3293             modmade = true;
3294         }
3295     }
3297     g_free(namespaced_name);
3299     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3301     sp_arctb_sensitivize( tbl, adj->value, other->value );
3303     if (modmade) {
3304         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3305                                    _("Arc: Change start/end"));
3306     }
3308     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3312 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3314     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3317 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3319     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3322 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3324     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3325     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3326         if ( ege_select_one_action_get_active( act ) != 0 ) {
3327             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3328         } else {
3329             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3330         }
3331     }
3333     // quit if run by the attr_changed listener
3334     if (g_object_get_data( tbl, "freeze" )) {
3335         return;
3336     }
3338     // in turn, prevent listener from responding
3339     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3341     bool modmade = false;
3343     if ( ege_select_one_action_get_active(act) != 0 ) {
3344         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3345              items != NULL;
3346              items = items->next)
3347         {
3348             if (SP_IS_ARC((SPItem *) items->data)) {
3349                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3350                 repr->setAttribute("sodipodi:open", "true");
3351                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3352                 modmade = true;
3353             }
3354         }
3355     } else {
3356         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3357              items != NULL;
3358              items = items->next)
3359         {
3360             if (SP_IS_ARC((SPItem *) items->data))    {
3361                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3362                 repr->setAttribute("sodipodi:open", NULL);
3363                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3364                 modmade = true;
3365             }
3366         }
3367     }
3369     if (modmade) {
3370         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3371                                    _("Arc: Change open/closed"));
3372     }
3374     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3377 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3379     GtkAdjustment *adj;
3380     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3381     gtk_adjustment_set_value(adj, 0.0);
3382     gtk_adjustment_value_changed(adj);
3384     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3385     gtk_adjustment_set_value(adj, 0.0);
3386     gtk_adjustment_value_changed(adj);
3388     spinbutton_defocus( GTK_OBJECT(obj) );
3391 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
3392                                       gchar const *old_value, gchar const *new_value,
3393                                       bool is_interactive, gpointer data)
3395     GObject *tbl = G_OBJECT(data);
3397     // quit if run by the _changed callbacks
3398     if (g_object_get_data( tbl, "freeze" )) {
3399         return;
3400     }
3402     // in turn, prevent callbacks from responding
3403     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3405     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3406     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3408     GtkAdjustment *adj1,*adj2;
3409     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3410     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3411     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3412     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3414     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3416     char const *openstr = NULL;
3417     openstr = repr->attribute("sodipodi:open");
3418     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3420     if (openstr) {
3421         ege_select_one_action_set_active( ocb, 1 );
3422     } else {
3423         ege_select_one_action_set_active( ocb, 0 );
3424     }
3426     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3429 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3430     NULL, /* child_added */
3431     NULL, /* child_removed */
3432     arc_tb_event_attr_changed,
3433     NULL, /* content_changed */
3434     NULL  /* order_changed */
3435 };
3438 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3440     int n_selected = 0;
3441     Inkscape::XML::Node *repr = NULL;
3443     purge_repr_listener( tbl, tbl );
3445     for (GSList const *items = selection->itemList();
3446          items != NULL;
3447          items = items->next)
3448     {
3449         if (SP_IS_ARC((SPItem *) items->data)) {
3450             n_selected++;
3451             repr = SP_OBJECT_REPR((SPItem *) items->data);
3452         }
3453     }
3455     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3457     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3458     if (n_selected == 0) {
3459         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3460     } else if (n_selected == 1) {
3461         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3462         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3464         if (repr) {
3465             g_object_set_data( tbl, "repr", repr );
3466             Inkscape::GC::anchor(repr);
3467             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3468             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3469         }
3470     } else {
3471         // FIXME: implement averaging of all parameters for multiple selected
3472         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3473         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3474         sp_arctb_sensitivize( tbl, 1, 0 );
3475     }
3479 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3481     EgeAdjustmentAction* eact = 0;
3484     {
3485         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3486         ege_output_action_set_use_markup( act, TRUE );
3487         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3488         g_object_set_data( holder, "mode_action", act );
3489     }
3491     /* Start */
3492     {
3493         eact = create_adjustment_action( "ArcStartAction",
3494                                          _("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
3495                                          "tools.shapes.arc", "start", 0.0,
3496                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3497                                          -360.0, 360.0, 1.0, 10.0,
3498                                          0, 0, 0,
3499                                          sp_arctb_start_value_changed);
3500         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3501     }
3503     /* End */
3504     {
3505         eact = create_adjustment_action( "ArcEndAction",
3506                                          _("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
3507                                          "tools.shapes.arc", "end", 0.0,
3508                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3509                                          -360.0, 360.0, 1.0, 10.0,
3510                                          0, 0, 0,
3511                                          sp_arctb_end_value_changed);
3512         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3513     }
3515     /* Segments / Pie checkbox */
3516     {
3517         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3519         GtkTreeIter iter;
3520         gtk_list_store_append( model, &iter );
3521         gtk_list_store_set( model, &iter,
3522                             0, _("Closed arc"),
3523                             1, _("Switch to segment (closed shape with two radii)"),
3524                             2, "circle_closed_arc",
3525                             -1 );
3527         gtk_list_store_append( model, &iter );
3528         gtk_list_store_set( model, &iter,
3529                             0, _("Open Arc"),
3530                             1, _("Switch to arc (unclosed shape)"),
3531                             2, "circle_open_arc",
3532                             -1 );
3534         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3535         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3536         g_object_set_data( holder, "open_action", act );
3538         ege_select_one_action_set_appearance( act, "full" );
3539         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3540         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3541         ege_select_one_action_set_icon_column( act, 2 );
3542         ege_select_one_action_set_tooltip_column( act, 1  );
3544         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3545         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3546         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3547         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3548     }
3550     /* Make Whole */
3551     {
3552         InkAction* inky = ink_action_new( "ArcResetAction",
3553                                           _("Make whole"),
3554                                           _("Make the shape a whole ellipse, not arc or segment"),
3555                                           "reset_circle",
3556                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3557         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3558         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3559         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3560         g_object_set_data( holder, "make_whole", inky );
3561     }
3563     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3564     // sensitivize make whole and open checkbox
3565     {
3566         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3567         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3568         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3569     }
3572     sigc::connection *connection = new sigc::connection(
3573         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3574         );
3575     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3576     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3582 // toggle button callbacks and updaters
3584 //########################
3585 //##      Dropper       ##
3586 //########################
3588 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3589     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3590     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3591     if ( set_action ) {
3592         if ( gtk_toggle_action_get_active( act ) ) {
3593             gtk_action_set_sensitive( set_action, TRUE );
3594         } else {
3595             gtk_action_set_sensitive( set_action, FALSE );
3596         }
3597     }
3599     spinbutton_defocus(GTK_OBJECT(tbl));
3602 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3603     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3604     spinbutton_defocus(GTK_OBJECT(tbl));
3608 /**
3609  * Dropper auxiliary toolbar construction and setup.
3610  *
3611  * TODO: Would like to add swatch of current color.
3612  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3613  *       can drag and drop places. Will provide a nice mixing palette.
3614  */
3615 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3617     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3619     {
3620         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3621                                                       _("Pick alpha"),
3622                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3623                                                       "color_alpha_get",
3624                                                       Inkscape::ICON_SIZE_DECORATION );
3625         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3626         g_object_set_data( holder, "pick_action", act );
3627         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3628         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3629     }
3631     {
3632         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3633                                                       _("Set alpha"),
3634                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3635                                                       "color_alpha_set",
3636                                                       Inkscape::ICON_SIZE_DECORATION );
3637         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3638         g_object_set_data( holder, "set_action", act );
3639         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3640         // make sure it's disabled if we're not picking alpha
3641         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3642         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3643     }
3647 //########################
3648 //##    Text Toolbox    ##
3649 //########################
3650 /*
3651 static void
3652 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3654     //Call back for letter sizing spinbutton
3657 static void
3658 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3660     //Call back for line height spinbutton
3663 static void
3664 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3666     //Call back for horizontal kerning spinbutton
3669 static void
3670 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3672     //Call back for vertical kerning spinbutton
3675 static void
3676 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3678     //Call back for letter rotation spinbutton
3679 }*/
3681 namespace {
3683 bool visible = false;
3685 void
3686 sp_text_toolbox_selection_changed (Inkscape::Selection *selection, GObject *tbl)
3688     SPStyle *query =
3689         sp_style_new (SP_ACTIVE_DOCUMENT);
3691     int result_family =
3692         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3694     int result_style =
3695         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3697     int result_numbers =
3698         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3700     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3702     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3703     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3704     {
3705         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3707         if (repr)
3708         {
3709             sp_style_read_from_repr (query, repr);
3710         }
3711         else
3712         {
3713             return;
3714         }
3715     }
3717     if (query->text)
3718     {
3719         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3720             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3721             gtk_entry_set_text (GTK_ENTRY (entry), "");
3723         } else if (query->text->font_family.value) {
3725             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3726             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3728             Gtk::TreePath path;
3729             try {
3730                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3731             } catch (...) {
3732                 return;
3733             }
3735             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3736             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3738             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3740             gtk_tree_selection_select_path (tselection, path.gobj());
3741             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3743             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3744         }
3746         //Size
3747         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3748         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3749         g_object_set_data (tbl, "size-block", gpointer(1));
3750         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3751         g_object_set_data (tbl, "size-block", gpointer(0));
3752         free (str);
3754         //Anchor
3755         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3756         {
3757             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3758             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3759             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3760             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3761         }
3762         else
3763         {
3764             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3765             {
3766                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3767                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3768                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3769                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3770             }
3771             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3772             {
3773                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3774                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3775                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3776                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3777             }
3778             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3779             {
3780                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3781                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3782                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3783                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3784             }
3785         }
3787         //Style
3788         {
3789             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3791             gboolean active = gtk_toggle_button_get_active (button);
3792             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3794             if (active != check)
3795             {
3796                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3797                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3798                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3799             }
3800         }
3802         {
3803             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3805             gboolean active = gtk_toggle_button_get_active (button);
3806             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3808             if (active != check)
3809             {
3810                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3811                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3812                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3813             }
3814         }
3816         //Orientation
3817         //locking both buttons, changing one affect all group (both)
3818         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3819         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3821         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3822         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3824         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3825         {
3826             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3827         }
3828         else
3829         {
3830             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3831         }
3832         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3833         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3834     }
3836     sp_style_unref(query);
3839 void
3840 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint flags, GObject *tbl)
3842     sp_text_toolbox_selection_changed (selection, tbl);
3845 void
3846 sp_text_toolbox_subselection_changed (gpointer dragger, GObject *tbl)
3848     sp_text_toolbox_selection_changed (NULL, tbl);
3851 void
3852 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3853                                 GObject             *tbl)
3855     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3856     GtkTreeModel *model = 0;
3857     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3858     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3859     GtkTreeIter   iter;
3860     char         *family = 0;
3862     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3863     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3865     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3866         return;
3867     }
3869     gtk_tree_model_get (model, &iter, 0, &family, -1);
3871     if (g_object_get_data (G_OBJECT (selection), "block"))
3872     {
3873         gtk_entry_set_text (GTK_ENTRY (entry), family);
3874         return;
3875     }
3877     gtk_widget_hide (popdown);
3878     visible = false;
3880     gtk_entry_set_text (GTK_ENTRY (entry), family);
3882     SPStyle *query =
3883         sp_style_new (SP_ACTIVE_DOCUMENT);
3885     int result_numbers =
3886         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3888     SPCSSAttr *css = sp_repr_css_attr_new ();
3889     sp_repr_css_set_property (css, "font-family", family);
3891     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3892     if (result_numbers == QUERY_STYLE_NOTHING)
3893     {
3894         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3895         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3896     }
3897     else
3898     {
3899         sp_desktop_set_style (desktop, css, true, true);
3900     }
3902     sp_style_unref(query);
3904     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3905                                    _("Text: Change font family"));
3906     sp_repr_css_attr_unref (css);
3907     free (family);
3908     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3910     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3913 void
3914 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3915                                        GObject      *tbl)
3917     const char *family = gtk_entry_get_text (entry);
3919     try {
3920         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3921         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3922         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3923         gtk_tree_selection_select_path (selection, path.gobj());
3924         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3925         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3926     } catch (...) {
3927         if (family && strlen (family))
3928         {
3929             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3930         }
3931     }
3934 void
3935 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3936                                    gpointer          data)
3938     if (g_object_get_data (G_OBJECT (button), "block")) return;
3939     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3940     int prop = GPOINTER_TO_INT(data);
3942     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3943     SPCSSAttr *css = sp_repr_css_attr_new ();
3945     switch (prop)
3946     {
3947         case 0:
3948         {
3949             sp_repr_css_set_property (css, "text-anchor", "start");
3950             sp_repr_css_set_property (css, "text-align", "start");
3951             break;
3952         }
3953         case 1:
3954         {
3955             sp_repr_css_set_property (css, "text-anchor", "middle");
3956             sp_repr_css_set_property (css, "text-align", "center");
3957             break;
3958         }
3960         case 2:
3961         {
3962             sp_repr_css_set_property (css, "text-anchor", "end");
3963             sp_repr_css_set_property (css, "text-align", "end");
3964             break;
3965         }
3967         case 3:
3968         {
3969             sp_repr_css_set_property (css, "text-anchor", "start");
3970             sp_repr_css_set_property (css, "text-align", "justify");
3971             break;
3972         }
3973     }
3975     SPStyle *query =
3976         sp_style_new (SP_ACTIVE_DOCUMENT);
3977     int result_numbers =
3978         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3980     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3981     if (result_numbers == QUERY_STYLE_NOTHING)
3982     {
3983         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3984     }
3986     sp_style_unref(query);
3988     sp_desktop_set_style (desktop, css, true, true);
3989     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3990                                    _("Text: Change alignment"));
3991     sp_repr_css_attr_unref (css);
3993     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3996 void
3997 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
3998                                gpointer          data)
4000     if (g_object_get_data (G_OBJECT (button), "block")) return;
4002     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4003     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4004     int          prop       = GPOINTER_TO_INT(data);
4005     bool         active     = gtk_toggle_button_get_active (button);
4008     switch (prop)
4009     {
4010         case 0:
4011         {
4012             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4013             break;
4014         }
4016         case 1:
4017         {
4018             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4019             break;
4020         }
4021     }
4023     SPStyle *query =
4024         sp_style_new (SP_ACTIVE_DOCUMENT);
4025     int result_numbers =
4026         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4028     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4029     if (result_numbers == QUERY_STYLE_NOTHING)
4030     {
4031         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4032     }
4034     sp_style_unref(query);
4036     sp_desktop_set_style (desktop, css, true, true);
4037     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4038                                    _("Text: Change font style"));
4039     sp_repr_css_attr_unref (css);
4041     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4044 void
4045 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4046                                      gpointer         data)
4048     if (g_object_get_data (G_OBJECT (button), "block")) {
4049         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4050         return;
4051     }
4053     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4054     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4055     int          prop       = GPOINTER_TO_INT(data);
4057     switch (prop)
4058     {
4059         case 0:
4060         {
4061             sp_repr_css_set_property (css, "writing-mode", "lr");
4062             break;
4063         }
4065         case 1:
4066         {
4067             sp_repr_css_set_property (css, "writing-mode", "tb");
4068             break;
4069         }
4070     }
4072     SPStyle *query =
4073         sp_style_new (SP_ACTIVE_DOCUMENT);
4074     int result_numbers =
4075         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4077     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4078     if (result_numbers == QUERY_STYLE_NOTHING)
4079     {
4080         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4081     }
4083     sp_desktop_set_style (desktop, css, true, true);
4084     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4085                                    _("Text: Change orientation"));
4086     sp_repr_css_attr_unref (css);
4088     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4091 gboolean
4092 sp_text_toolbox_size_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
4094     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4095     if (!desktop) return FALSE;
4097     switch (get_group0_keyval (event)) {
4098         case GDK_Escape: // defocus
4099             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4100             return TRUE; // I consumed the event
4101             break;
4102         case GDK_Return: // defocus
4103         case GDK_KP_Enter:
4104             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4105             return TRUE; // I consumed the event
4106             break;
4107     }
4108     return FALSE;
4111 gboolean
4112 sp_text_toolbox_family_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
4114     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4115     if (!desktop) return FALSE;
4117     switch (get_group0_keyval (event)) {
4118         case GDK_Escape: // defocus
4119             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4120             sp_text_toolbox_selection_changed (NULL, tbl); // update
4121             return TRUE; // I consumed the event
4122             break;
4123     }
4124     return FALSE;
4127 gboolean
4128 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
4130     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4131     if (!desktop) return FALSE;
4133     switch (get_group0_keyval (event)) {
4134         case GDK_Escape: // defocus
4135             gtk_widget_hide (w);
4136             visible = false;
4137             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4138             return TRUE; // I consumed the event
4139             break;
4140     }
4141     return FALSE;
4145 void
4146 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4147                                GObject     *tbl)
4149     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4151     if (g_object_get_data (tbl, "size-block")) return;
4153 #if GTK_CHECK_VERSION(2,6,0)
4154     char *text = gtk_combo_box_get_active_text (cbox);
4155 #else // GTK_CHECK_VERSION(2,6,0)
4156     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
4157     GtkTreeIter iter;
4158     char *text = NULL;
4160     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
4161         gtk_tree_model_get (model, &iter, 0, &text, -1);
4162 #endif // GTK_CHECK_VERSION(2,6,0)
4164     SPCSSAttr *css = sp_repr_css_attr_new ();
4165     sp_repr_css_set_property (css, "font-size", text);
4166     free (text);
4168     SPStyle *query =
4169         sp_style_new (SP_ACTIVE_DOCUMENT);
4170     int result_numbers =
4171         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4173     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4174     if (result_numbers == QUERY_STYLE_NOTHING)
4175     {
4176         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4177     }
4179     sp_style_unref(query);
4181     sp_desktop_set_style (desktop, css, true, true);
4182     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4183                                    _("Text: Change font size"));
4184     sp_repr_css_attr_unref (css);
4187     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4188         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4191 void
4192 sp_text_toolbox_text_popdown_clicked    (GtkButton          *button,
4193                                          GObject            *tbl)
4195     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4196     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4197     int x, y;
4199     if (!visible)
4200     {
4201         gdk_window_get_origin (widget->window, &x, &y);
4202         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4203         gtk_widget_show_all (popdown);
4205         gdk_pointer_grab (widget->window, TRUE,
4206                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4207                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4208                                         GDK_POINTER_MOTION_MASK),
4209                           NULL, NULL, GDK_CURRENT_TIME);
4211         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4213         visible = true;
4214     }
4215     else
4216     {
4217         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4218         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4219         gtk_widget_hide (popdown);
4220         visible = false;
4221     }
4224 gboolean
4225 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4226                                  GdkEventFocus    *event,
4227                                  GObject          *tbl)
4229     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4230     return FALSE;
4233 gboolean
4234 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4235                                    GdkEventFocus    *event,
4236                                    GObject          *tbl)
4238     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4240     gtk_widget_hide (popdown);
4241     visible = false;
4242     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4243     return TRUE;
4246 void
4247 cell_data_func  (GtkTreeViewColumn *column,
4248                  GtkCellRenderer   *cell,
4249                  GtkTreeModel      *tree_model,
4250                  GtkTreeIter       *iter,
4251                  gpointer           data)
4253     char        *family,
4254         *family_escaped,
4255         *sample_escaped;
4257     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4259     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4261     family_escaped = g_markup_escape_text (family, -1);
4262     sample_escaped = g_markup_escape_text (sample, -1);
4264     std::stringstream markup;
4265     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4266     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4268     free (family);
4269     free (family_escaped);
4270     free (sample_escaped);
4273 static void delete_completion(GObject *obj, GtkWidget *entry) {
4274     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4275     if (completion) {
4276         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4277         g_object_unref (completion);
4278     }
4281 GtkWidget*
4282 sp_text_toolbox_new (SPDesktop *desktop)
4284     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4286     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4287     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4289     GtkTooltips *tt = gtk_tooltips_new();
4290     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4292     ////////////Family
4293     //Window
4294     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4295     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4297     //Entry
4298     GtkWidget           *entry = gtk_entry_new ();
4299     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4300     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4301     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4302     gtk_entry_completion_set_text_column (completion, 0);
4303     gtk_entry_completion_set_minimum_key_length (completion, 1);
4304     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4305     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4306     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4307     aux_toolbox_space (tbl, 1);
4308     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4309     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4311     //Button
4312     GtkWidget   *button = gtk_button_new ();
4313     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4314     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4316     //Popdown
4317     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4318     GtkWidget           *treeview = gtk_tree_view_new ();
4320     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4321     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4322     gtk_tree_view_column_pack_start (column, cell, FALSE);
4323     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4324     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4325     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4327     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4328     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4329 #if GTK_CHECK_VERSION(2,6,0)
4330     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4331 #endif // GTK_CHECK_VERSION(2,6,0)
4333     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4335     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4336     gtk_container_add (GTK_CONTAINER (sw), treeview);
4338     gtk_container_add (GTK_CONTAINER (window), sw);
4339     gtk_widget_set_size_request (window, 300, 450);
4341     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4342     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4343     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4345     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4347     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4348     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4350     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4351     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4353     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4354     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4355     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4356     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4357     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4359     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4360     aux_toolbox_space (tbl, 1);
4361     GtkWidget *box = gtk_event_box_new ();
4362     gtk_container_add (GTK_CONTAINER (box), image);
4363     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4364     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4365     GtkTooltips *tooltips = gtk_tooltips_new ();
4366     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4367     gtk_widget_hide (GTK_WIDGET (box));
4368     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4370     ////////////Size
4371     const char *sizes[] = {
4372         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4373         "16", "18", "20", "22", "24", "28",
4374         "32", "36", "40", "48", "56", "64", "72", "144"
4375     };
4377     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4378     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4379     gtk_widget_set_size_request (cbox, 80, -1);
4380     aux_toolbox_space (tbl, 1);
4381     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4382     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4383     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4384     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4386     //spacer
4387     aux_toolbox_space (tbl, 4);
4388     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4390     ////////////Text anchor
4391     GtkWidget *group   = gtk_radio_button_new (NULL);
4392     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4393     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4395     // left
4396     GtkWidget *rbutton = group;
4397     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4398     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4399     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4401     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4402     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4403     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4404     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4406     // center
4407     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4408     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4409     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4410     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4412     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4413     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4414     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4415     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4417     // right
4418     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4419     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4420     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4421     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4423     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4424     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4425     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4426     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4428     // fill
4429     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4430     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4431     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4432     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4434     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4435     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4436     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4437     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4439     aux_toolbox_space (tbl, 1);
4440     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4442     //spacer
4443     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4445     ////////////Text style
4446     row = gtk_hbox_new (FALSE, 4);
4448     // bold
4449     rbutton = gtk_toggle_button_new ();
4450     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4451     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4452     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4453     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4455     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4456     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4457     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4459     // italic
4460     rbutton = gtk_toggle_button_new ();
4461     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4462     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4463     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4464     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4466     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4467     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4468     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4470     aux_toolbox_space (tbl, 1);
4471     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4473     //spacer
4474     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4476     ////////////Text orientation
4477     group   = gtk_radio_button_new (NULL);
4478     row     = gtk_hbox_new (FALSE, 4);
4479     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4481     // horizontal
4482     rbutton = group;
4483     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4484     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4485     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4486     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4488     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4489     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4490     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4492     // vertical
4493     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4494     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4495     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4496     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4497     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4499     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4500     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4501     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4502     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4505     //watch selection
4506     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4508     sigc::connection *c_selection_changed =
4509         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4510                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4511     pool->add_connection ("selection-changed", c_selection_changed);
4513     sigc::connection *c_selection_modified =
4514         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4515                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4516     pool->add_connection ("selection-modified", c_selection_modified);
4518     sigc::connection *c_subselection_changed =
4519         new sigc::connection (desktop->connectToolSubselectionChanged
4520                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4521     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4523     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4526 #if 0
4527     // horizontal
4528     {
4529         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4530         GtkWidget *b = group = gtk_radio_button_new (NULL);
4531         gtk_container_add (GTK_CONTAINER (b), px);
4532         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4533         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4534         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4535         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4536     }
4538     // vertical
4539     {
4540         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4541         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4542         gtk_container_add (GTK_CONTAINER (b), px);
4543         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4544         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4545         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4546         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4547     }
4549     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4551     // letter spacing
4552     {
4553         {
4554             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4555             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4556             gtk_container_add (GTK_CONTAINER (hb), image);
4557             gtk_widget_show(image);
4558             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4559         }
4561         {
4562             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between letters"),
4563                                              "tools.text", "letter_spacing", 0.0,
4564                                              us, tbl, FALSE, NULL,
4565                                              -1000.0, 1000.0, 0.1, 0.1,
4566                                              sp_text_letter_changed, 0.1, 1);
4567             gtk_widget_set_size_request (hb, 45, 6);
4568             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4569         }
4570     }
4572     // line spacing
4573     {
4574         {
4575             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4576             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4577             gtk_container_add (GTK_CONTAINER (hb), image);
4578             gtk_widget_show(image);
4579             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4580         }
4582         {
4583             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between lines"),
4584                                              "tools.text", "line_spacing", 0,
4585                                              us, tbl, FALSE, NULL,
4586                                              -1000.0, 1000.0, 0.1, 0.1,
4587                                              sp_text_line_changed, 0.1, 1);
4588             gtk_widget_set_size_request (hb, 45, 0);
4589             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4590         }
4591     }
4593     {
4594         // horizontal kerning/vertical kerning units menu: create
4595         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4596         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4597         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4599         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4601         // horizontal kerning
4602         {
4603             {
4604                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4605                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4606                 gtk_container_add (GTK_CONTAINER (hb), image);
4607                 gtk_widget_show(image);
4608                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4609             }
4611             {
4612                 GtkWidget *hb = sp_tb_spinbutton((""), _("Horizontal kerning"),
4613                                                  "tools.text", "horizontal_kerning", 0,
4614                                                  us, tbl, FALSE, NULL,
4615                                                  -100.00, 100.00, 0.01, 0.1,
4616                                                  sp_text_horiz_kern_changed);
4617                 gtk_widget_set_size_request (hb, 45, 0);
4618                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4619             }
4620         }
4622         // vertical kerning
4623         {
4624             {
4625                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4626                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4627                 gtk_container_add (GTK_CONTAINER (hb), image);
4628                 gtk_widget_show(image);
4629                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4630             }
4632             {
4633                 GtkWidget *hb = sp_tb_spinbutton((""), _("Vertical kerning"),
4634                                                  "tools.text", "vertical_kerning", 0,
4635                                                  us, tbl, FALSE, NULL,
4636                                                  -100.00, 100.00, 0.01, 0.1,
4637                                                  sp_text_vert_kern_changed);
4638                 gtk_widget_set_size_request (hb, 45, 0);
4639                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4640             }
4641         }
4643         // add the units menu
4644         gtk_widget_show(us);
4645         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4646         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4647     }
4649     // letter rotation
4650     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4651     {
4652         {
4653             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4654             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4655             gtk_container_add (GTK_CONTAINER (hb), image);
4656             gtk_widget_show(image);
4657             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4658         }
4659         {
4660             GtkWidget *hb = sp_tb_spinbutton((""), _("Letter rotation"),
4661                                              "tools.text", "letter_rotation", 0,
4662                                              us, tbl, FALSE, NULL,
4663                                              -180.0, 180.0, 0.1, 0.1,
4664                                              sp_text_letter_rotation_changed, 0.1, 1);
4665             gtk_widget_set_size_request (hb, 45, 0);
4666             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4667         }
4668         // rotation degree label
4669         {
4670             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4671             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4672         }
4673     }
4675     // Remove Manual Kerns
4676     {
4677         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4678         GtkWidget *button = gtk_button_new ();
4679         gtk_container_add (GTK_CONTAINER (button), px);
4680         gtk_widget_show(button);
4681         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4682         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4683         gtk_widget_set_sensitive(button, TRUE);
4684         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4685     }
4686 #endif
4688     gtk_widget_show_all (tbl);
4689     return tbl;
4691 } // end of sp_text_toolbox_new()
4693 }//<unnamed> namespace
4696 //#########################
4697 //##      Connector      ##
4698 //#########################
4700 static void sp_connector_path_set_avoid(void)
4702     cc_selection_set_avoid(true);
4706 static void sp_connector_path_set_ignore(void)
4708     cc_selection_set_avoid(false);
4713 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4715     // quit if run by the _changed callbacks
4716     if (g_object_get_data( tbl, "freeze" )) {
4717         return;
4718     }
4720     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4721     SPDocument *doc = sp_desktop_document(desktop);
4723     if (!sp_document_get_undo_sensitive(doc))
4724     {
4725         return;
4726     }
4728     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4730     if ( repr->attribute("inkscape:connector-spacing") ) {
4731         gdouble priorValue = gtk_adjustment_get_value(adj);
4732         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4733         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4734             return;
4735         }
4736     } else if ( adj->value == defaultConnSpacing ) {
4737         return;
4738     }
4740     // in turn, prevent callbacks from responding
4741     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4743     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4744     SP_OBJECT(desktop->namedview)->updateRepr();
4746     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4747     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4748         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4749         NR::Matrix m = NR::identity();
4750         avoid_item_move(&m, item);
4751     }
4753     if (items) {
4754         g_slist_free(items);
4755     }
4757     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4758             _("Change connector spacing"));
4760     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4762     spinbutton_defocus(GTK_OBJECT(tbl));
4765 static void sp_connector_graph_layout(void)
4767     if (!SP_ACTIVE_DESKTOP) return;
4769     // hack for clones, see comment in align-and-distribute.cpp
4770     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4771     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4773     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4775     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4777     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4780 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4782     if ( gtk_toggle_action_get_active( act ) ) {
4783         prefs_set_string_attribute("tools.connector", "directedlayout",
4784                 "true");
4785     } else {
4786         prefs_set_string_attribute("tools.connector", "directedlayout",
4787                 "false");
4788     }
4791 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4793     if ( gtk_toggle_action_get_active( act ) ) {
4794         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4795                 "true");
4796     } else {
4797         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4798                 "false");
4799     }
4803 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4805     prefs_set_double_attribute("tools.connector", "length", adj->value);
4806     spinbutton_defocus(GTK_OBJECT(tbl));
4809 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4810         gchar const *name, gchar const *old_value, gchar const *new_value,
4811         bool is_interactive, gpointer data)
4813     GtkWidget *tbl = GTK_WIDGET(data);
4815     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4816         return;
4817     }
4818     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4819         return;
4820     }
4822     GtkAdjustment *adj = (GtkAdjustment*)
4823             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4824     gdouble spacing = defaultConnSpacing;
4825     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4827     gtk_adjustment_set_value(adj, spacing);
4831 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4832     NULL, /* child_added */
4833     NULL, /* child_removed */
4834     connector_tb_event_attr_changed,
4835     NULL, /* content_changed */
4836     NULL  /* order_changed */
4837 };
4840 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4842     {
4843         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4844                                           _("Avoid"),
4845                                           _("Make connectors avoid selected objects"),
4846                                           "connector_avoid",
4847                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4848         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
4849         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4850     }
4852     {
4853         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
4854                                           _("Ignore"),
4855                                           _("Make connectors ignore selected objects"),
4856                                           "connector_ignore",
4857                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4858         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
4859         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4860     }
4862     EgeAdjustmentAction* eact = 0;
4864     // Spacing spinbox
4865     eact = create_adjustment_action( "ConnectorSpacingAction",
4866                                      _("Spacing:"), _("The amount of space left around objects by auto-routing connectors"),
4867                                      "tools.connector", "spacing", defaultConnSpacing,
4868                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
4869                                      0, 100, 1.0, 10.0,
4870                                      0, 0, 0,
4871                                      connector_spacing_changed, 1, 0 );
4872     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4874     // Graph (connector network) layout
4875     {
4876         InkAction* inky = ink_action_new( "ConnectorGraphAction",
4877                                           _("Graph"),
4878                                           _("Nicely arrange selected connector network"),
4879                                           "graph_layout",
4880                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4881         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
4882         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4883     }
4885     // Default connector length spinbox
4886     eact = create_adjustment_action( "ConnectorLengthAction",
4887                                      _("Length:"), _("Ideal length for connectors when layout is applied"),
4888                                      "tools.connector", "length", 100,
4889                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
4890                                      10, 1000, 10.0, 100.0,
4891                                      0, 0, 0,
4892                                      connector_length_changed, 1, 0 );
4893     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4896     // Directed edges toggle button
4897     {
4898         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
4899                                                       _("Downwards"),
4900                                                       _("Make connectors with end-markers (arrows) point downwards"),
4901                                                       "directed_graph",
4902                                                       Inkscape::ICON_SIZE_DECORATION );
4903         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4905         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
4906         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4907                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4909         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
4910     }
4912     // Avoid overlaps toggle button
4913     {
4914         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
4915                                                       _("Remove overlaps"),
4916                                                       _("Do not allow overlapping shapes"),
4917                                                       "remove_overlaps",
4918                                                       Inkscape::ICON_SIZE_DECORATION );
4919         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4921         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
4922         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4923                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4925         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
4926     }
4928     // Code to watch for changes to the connector-spacing attribute in
4929     // the XML.
4930     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4931     g_assert(repr != NULL);
4933     purge_repr_listener( holder, holder );
4935     if (repr) {
4936         g_object_set_data( holder, "repr", repr );
4937         Inkscape::GC::anchor(repr);
4938         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
4939         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
4940     }
4941 } // end of sp_connector_toolbox_prep()
4944 //#########################
4945 //##     Paintbucket     ##
4946 //#########################
4948 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* tbl)
4950     gint channels = ege_select_one_action_get_active( act );
4951     flood_channels_set_channels( channels );
4954 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject *tbl)
4956     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4959 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject *tbl)
4961     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
4964 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
4966     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
4967     SPUnit const *unit = tracker->getActiveUnit();
4970     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4973 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
4975     // FIXME: make defaults settable via Inkscape Options
4976     struct KeyValue {
4977         char const *key;
4978         double value;
4979     } const key_values[] = {
4980         {"threshold", 15},
4981         {"offset", 0.0}
4982     };
4984     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
4985         KeyValue const &kv = key_values[i];
4986         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
4987         if ( adj ) {
4988             gtk_adjustment_set_value(adj, kv.value);
4989         }
4990     }
4991     
4992     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
4993     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
4994     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
4995     ege_select_one_action_set_active( autogap_action, 0 );
4998 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5000     EgeAdjustmentAction* eact = 0;
5002     {
5003         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5005         GList* items = 0;
5006         gint count = 0;
5007         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5008         {
5009             GtkTreeIter iter;
5010             gtk_list_store_append( model, &iter );
5011             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5012             count++;
5013         }
5014         g_list_free( items );
5015         items = 0;
5016         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by:"), (""), NULL, GTK_TREE_MODEL(model) );
5017         ege_select_one_action_set_appearance( act1, "compact" );
5018         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5019         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5020         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5021         g_object_set_data( holder, "channels_action", act1 );
5022     }
5024     // Spacing spinbox
5025     {
5026         eact = create_adjustment_action(
5027             "ThresholdAction",
5028             _("Threshold:"),
5029             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5030             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5031             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5032             0, 0, 0,
5033             paintbucket_threshold_changed, 1, 0 );
5035         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5036     }
5038     // Create the units menu.
5039     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5040     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
5041     g_object_set_data( holder, "tracker", tracker );
5042     {
5043         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5044         gtk_action_group_add_action( mainActions, act );
5045     }
5047     // Offset spinbox
5048     {
5049         eact = create_adjustment_action(
5050             "OffsetAction",
5051             _("Grow/shrink by:"),
5052             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5053             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5054             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5055             0, 0, 0,
5056             paintbucket_offset_changed, 1, 2);
5057         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5059         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5060     }
5061     
5062     /* Auto Gap */
5063     {
5064         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5066         GList* items = 0;
5067         gint count = 0;
5068         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5069         {
5070             GtkTreeIter iter;
5071             gtk_list_store_append( model, &iter );
5072             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5073             count++;
5074         }
5075         g_list_free( items );
5076         items = 0;
5077         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps:"), (""), NULL, GTK_TREE_MODEL(model) );
5078         ege_select_one_action_set_appearance( act2, "compact" );
5079         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5080         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5081         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5082         g_object_set_data( holder, "autogap_action", act2 );
5083     }
5085     /* Reset */
5086     {
5087         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5088                                           _("Defaults"),
5089                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5090                                           GTK_STOCK_CLEAR );
5091         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5092         gtk_action_group_add_action( mainActions, act );
5093         gtk_action_set_sensitive( act, TRUE );
5094     }
5098 /*
5099   Local Variables:
5100   mode:c++
5101   c-file-style:"stroustrup"
5102   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5103   indent-tabs-mode:nil
5104   fill-column:99
5105   End:
5106 */
5107 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :