Code

add Affect: label, fill in presets for shape tools
[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 "sp-rect.h"
67 #include "box3d.h"
68 #include "box3d-context.h"
69 #include "sp-star.h"
70 #include "sp-spiral.h"
71 #include "sp-ellipse.h"
72 #include "sp-text.h"
73 #include "sp-flowtext.h"
74 #include "style.h"
75 #include "selection.h"
76 #include "document-private.h"
77 #include "desktop-style.h"
78 #include "../libnrtype/font-lister.h"
79 #include "../connection-pool.h"
80 #include "../prefs-utils.h"
81 #include "../inkscape-stock.h"
82 #include "icon.h"
83 #include "graphlayout/graphlayout.h"
85 #include "mod360.h"
87 #include "toolbox.h"
89 #include "flood-context.h"
91 #include "ink-action.h"
92 #include "ege-adjustment-action.h"
93 #include "ege-output-action.h"
94 #include "ege-select-one-action.h"
95 #include "helper/unit-tracker.h"
97 using Inkscape::UnitTracker;
99 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
100 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
102 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
103 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
115 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
121 static struct {
122     gchar const *type_name;
123     gchar const *data_name;
124     sp_verb_t verb;
125     sp_verb_t doubleclick_verb;
126 } const tools[] = {
127     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
128     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
129     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
130     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
131     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
132 //    { "SP3DBoxContext",    "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
133     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
134     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
135     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
136     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
137     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
138     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
139     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
140     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
141     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
142     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
143     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
144     { NULL, NULL, 0, 0 }
145 };
147 static struct {
148     gchar const *type_name;
149     gchar const *data_name;
150     GtkWidget *(*create_func)(SPDesktop *desktop);
151     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
152     gchar const *ui_name;
153     gint swatch_verb_id;
154     gchar const *swatch_tool;
155     gchar const *swatch_tip;
156 } const aux_toolboxes[] = {
157     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
158       SP_VERB_INVALID, 0, 0},
159     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
160       SP_VERB_INVALID, 0, 0},
161     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
162       SP_VERB_INVALID, 0, 0},
163     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
164       SP_VERB_INVALID, 0, 0},
165     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
166       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
167     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
168       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
169     { "SP3DBoxContext",  "3dbox_toolbox",  0, sp_3dbox_toolbox_prep,             "3DBoxToolbar",
170       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
171     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
172       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
173     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
174       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
175     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
176       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
177     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
178       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
179     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
180       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
181     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
182       SP_VERB_INVALID, 0, 0},
183     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
184       SP_VERB_INVALID, 0, 0},
185     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
186       SP_VERB_INVALID, 0, 0},
187     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
188       SP_VERB_INVALID, 0, 0},
189     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
190       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
191     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
192 };
195 static gchar const * ui_descr =
196         "<ui>"
197         "  <toolbar name='SelectToolbar'>"
198         "    <toolitem action='ObjectRotate90CCW' />"
199         "    <toolitem action='ObjectRotate90' />"
200         "    <toolitem action='ObjectFlipHorizontally' />"
201         "    <toolitem action='ObjectFlipVertically' />"
202         "    <separator />"
203         "    <toolitem action='SelectionToBack' />"
204         "    <toolitem action='SelectionLower' />"
205         "    <toolitem action='SelectionRaise' />"
206         "    <toolitem action='SelectionToFront' />"
207         "    <separator />"
208         "    <toolitem action='XAction' />"
209         "    <toolitem action='YAction' />"
210         "    <toolitem action='WidthAction' />"
211         "    <toolitem action='LockAction' />"
212         "    <toolitem action='HeightAction' />"
213         "    <toolitem action='UnitsAction' />"
214         "    <separator />"
215         "    <toolitem action='transform_affect_label' />"
216         "    <toolitem action='transform_stroke' />"
217         "    <toolitem action='transform_corners' />"
218         "    <toolitem action='transform_gradient' />"
219         "    <toolitem action='transform_pattern' />"
220         "  </toolbar>"
222         "  <toolbar name='NodeToolbar'>"
223         "    <toolitem action='NodeInsertAction' />"
224         "    <toolitem action='NodeDeleteAction' />"
225         "    <separator />"
226         "    <toolitem action='NodeJoinAction' />"
227         "    <toolitem action='NodeJoinSegmentAction' />"
228         "    <toolitem action='NodeDeleteSegmentAction' />"
229         "    <toolitem action='NodeBreakAction' />"
230         "    <separator />"
231         "    <toolitem action='NodeCuspAction' />"
232         "    <toolitem action='NodeSmoothAction' />"
233         "    <toolitem action='NodeSymmetricAction' />"
234         "    <separator />"
235         "    <toolitem action='NodeLineAction' />"
236         "    <toolitem action='NodeCurveAction' />"
237         "    <separator />"
238         "    <toolitem action='ObjectToPath' />"
239         "    <toolitem action='StrokeToPath' />"
240         "    <separator />"
241         "    <toolitem action='NodesShowHandlesAction' />"
242         "  </toolbar>"
244         "  <toolbar name='TweakToolbar'>"
245         "    <toolitem action='TweakWidthAction' />"
246         "    <toolitem action='TweakForceAction' />"
247         "    <separator />"
248         "    <toolitem action='TweakModeAction' />"
249         "    <separator />"
250         "    <toolitem action='TweakFidelityAction' />"
251         "    <toolitem action='TweakPressureAction' />"
252         "  </toolbar>"
254         "  <toolbar name='ZoomToolbar'>"
255         "    <toolitem action='ZoomIn' />"
256         "    <toolitem action='ZoomOut' />"
257         "    <separator />"
258         "    <toolitem action='ZoomSelection' />"
259         "    <toolitem action='ZoomDrawing' />"
260         "    <toolitem action='ZoomPage' />"
261         "    <toolitem action='ZoomPageWidth' />"
262         "    <separator />"
263         "    <toolitem action='ZoomPrev' />"
264         "    <toolitem action='ZoomNext' />"
265         "    <separator />"
266         "    <toolitem action='Zoom1:0' />"
267         "    <toolitem action='Zoom1:2' />"
268         "    <toolitem action='Zoom2:1' />"
269         "  </toolbar>"
271         "  <toolbar name='StarToolbar'>"
272         "    <separator />"
273         "    <toolitem action='StarStateAction' />"
274         "    <separator />"
275         "    <toolitem action='FlatAction' />"
276         "    <separator />"
277         "    <toolitem action='MagnitudeAction' />"
278         "    <toolitem action='SpokeAction' />"
279         "    <toolitem action='RoundednessAction' />"
280         "    <toolitem action='RandomizationAction' />"
281         "    <separator />"
282         "    <toolitem action='StarResetAction' />"
283         "  </toolbar>"
285         "  <toolbar name='RectToolbar'>"
286         "    <toolitem action='RectStateAction' />"
287         "    <toolitem action='RectWidthAction' />"
288         "    <toolitem action='RectHeightAction' />"
289         "    <toolitem action='RadiusXAction' />"
290         "    <toolitem action='RadiusYAction' />"
291         "    <toolitem action='RectUnitsAction' />"
292         "    <separator />"
293         "    <toolitem action='RectResetAction' />"
294         "  </toolbar>"
296         "  <toolbar name='3DBoxToolbar'>"
297         "    <toolitem action='3DBoxVPXAction' />"
298         "    <toolitem action='3DBoxVPYAction' />"
299         "    <toolitem action='3DBoxVPZAction' />"
300         "    <separator />"
301         "  </toolbar>"
303         "  <toolbar name='SpiralToolbar'>"
304         "    <toolitem action='SpiralStateAction' />"
305         "    <toolitem action='SpiralRevolutionAction' />"
306         "    <toolitem action='SpiralExpansionAction' />"
307         "    <toolitem action='SpiralT0Action' />"
308         "    <separator />"
309         "    <toolitem action='SpiralResetAction' />"
310         "  </toolbar>"
312         "  <toolbar name='PenToolbar'>"
313         "  </toolbar>"
315         "  <toolbar name='PencilToolbar'>"
316         "  </toolbar>"
318         "  <toolbar name='CalligraphyToolbar'>"
319         "    <separator />"
320         "    <toolitem action='CalligraphyWidthAction' />"
321         "    <toolitem action='ThinningAction' />"
322         "    <separator />"
323         "    <toolitem action='AngleAction' />"
324         "    <toolitem action='FixationAction' />"
325         "    <toolitem action='CapRoundingAction' />"
326         "    <separator />"
327         "    <toolitem action='TremorAction' />"
328         "    <toolitem action='WiggleAction' />"
329         "    <toolitem action='MassAction' />"
330         "    <separator />"
331         "    <toolitem action='TraceAction' />"
332         "    <toolitem action='PressureAction' />"
333         "    <toolitem action='TiltAction' />"
334         "    <toolitem action='CalligraphyResetAction' />"
335         "  </toolbar>"
337         "  <toolbar name='ArcToolbar'>"
338         "    <toolitem action='ArcStateAction' />"
339         "    <separator />"
340         "    <toolitem action='ArcStartAction' />"
341         "    <toolitem action='ArcEndAction' />"
342         "    <separator />"
343         "    <toolitem action='ArcOpenAction' />"
344         "    <separator />"
345         "    <toolitem action='ArcResetAction' />"
346         "    <separator />"
347         "  </toolbar>"
349         "  <toolbar name='PaintbucketToolbar'>"
350         "    <toolitem action='ChannelsAction' />"
351         "    <separator />"
352         "    <toolitem action='ThresholdAction' />"
353         "    <separator />"
354         "    <toolitem action='OffsetAction' />"
355         "    <toolitem action='PaintbucketUnitsAction' />"
356         "    <separator />"
357         "    <toolitem action='AutoGapAction' />"
358         "    <separator />"
359         "    <toolitem action='PaintbucketResetAction' />"
360         "  </toolbar>"
362         "  <toolbar name='DropperToolbar'>"
363         "    <toolitem action='DropperPickAlphaAction' />"
364         "    <toolitem action='DropperSetAlphaAction' />"
365         "  </toolbar>"
367         "  <toolbar name='ConnectorToolbar'>"
368         "    <toolitem action='ConnectorAvoidAction' />"
369         "    <toolitem action='ConnectorIgnoreAction' />"
370         "    <toolitem action='ConnectorSpacingAction' />"
371         "    <toolitem action='ConnectorGraphAction' />"
372         "    <toolitem action='ConnectorLengthAction' />"
373         "    <toolitem action='ConnectorDirectedAction' />"
374         "    <toolitem action='ConnectorOverlapAction' />"
375         "  </toolbar>"
377         "</ui>"
380 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
382 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
384 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
385 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
387 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
388 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
390 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
391 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
393 /* Global text entry widgets necessary for update */
394 /* GtkWidget *dropper_rgb_entry,
395           *dropper_opacity_entry ; */
396 // should be made a private member once this is converted to class
398 static void delete_connection(GObject *obj, sigc::connection *connection) {
399     connection->disconnect();
400     delete connection;
403 static void purge_repr_listener( GObject* obj, GObject* tbl )
405     (void)obj;
406     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
407     if (oldrepr) { // remove old listener
408         sp_repr_remove_listener_by_data(oldrepr, tbl);
409         Inkscape::GC::release(oldrepr);
410         oldrepr = 0;
411         g_object_set_data( tbl, "repr", NULL );
412     }
415 GtkWidget *
416 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
417                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
418                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
420     SPAction *action = verb->get_action(view);
421     if (!action) return NULL;
423     SPAction *doubleclick_action;
424     if (doubleclick_verb)
425         doubleclick_action = doubleclick_verb->get_action(view);
426     else
427         doubleclick_action = NULL;
429     /* fixme: Handle sensitive/unsensitive */
430     /* fixme: Implement sp_button_new_from_action */
431     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
432     gtk_widget_show(b);
433     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
435     return b;
438 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
439                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
441     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
444 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
445                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
447     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
451 static void trigger_sp_action( GtkAction* act, gpointer user_data )
453     SPAction* targetAction = SP_ACTION(user_data);
454     if ( targetAction ) {
455         sp_action_perform( targetAction, NULL );
456     }
459 static void sp_action_action_set_sensitive (SPAction *action, unsigned int sensitive, void *data)
461     if ( data ) {
462         GtkAction* act = GTK_ACTION(data);
463         gtk_action_set_sensitive( act, sensitive );
464     }
467 static SPActionEventVector action_event_vector = {
468     {NULL},
469     NULL,
470     NULL,
471     sp_action_action_set_sensitive,
472     NULL,
473     NULL
474 };
476 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
478     GtkAction* act = 0;
480     SPAction* targetAction = verb->get_action(view);
481     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
482     act = GTK_ACTION(inky);
483     gtk_action_set_sensitive( act, targetAction->sensitive );
485     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
487     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
488     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
490     return act;
493 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
495     Inkscape::UI::View::View *view = desktop;
496     gint verbsToUse[] = {
497         // disabled until we have icons for them:
498         //find
499         //SP_VERB_EDIT_TILE,
500         //SP_VERB_EDIT_UNTILE,
501         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
502         SP_VERB_DIALOG_DISPLAY,
503         SP_VERB_DIALOG_FILL_STROKE,
504         SP_VERB_DIALOG_NAMEDVIEW,
505         SP_VERB_DIALOG_TEXT,
506         SP_VERB_DIALOG_XML_EDITOR,
507         SP_VERB_EDIT_CLONE,
508         SP_VERB_EDIT_COPY,
509         SP_VERB_EDIT_CUT,
510         SP_VERB_EDIT_DUPLICATE,
511         SP_VERB_EDIT_PASTE,
512         SP_VERB_EDIT_REDO,
513         SP_VERB_EDIT_UNDO,
514         SP_VERB_EDIT_UNLINK_CLONE,
515         SP_VERB_FILE_EXPORT,
516         SP_VERB_FILE_IMPORT,
517         SP_VERB_FILE_NEW,
518         SP_VERB_FILE_OPEN,
519         SP_VERB_FILE_PRINT,
520         SP_VERB_FILE_SAVE,
521         SP_VERB_OBJECT_TO_CURVE,
522         SP_VERB_SELECTION_GROUP,
523         SP_VERB_SELECTION_OUTLINE,
524         SP_VERB_SELECTION_UNGROUP,
525         SP_VERB_ZOOM_1_1,
526         SP_VERB_ZOOM_1_2,
527         SP_VERB_ZOOM_2_1,
528         SP_VERB_ZOOM_DRAWING,
529         SP_VERB_ZOOM_IN,
530         SP_VERB_ZOOM_NEXT,
531         SP_VERB_ZOOM_OUT,
532         SP_VERB_ZOOM_PAGE,
533         SP_VERB_ZOOM_PAGE_WIDTH,
534         SP_VERB_ZOOM_PREV,
535         SP_VERB_ZOOM_SELECTION,
536     };
538     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
539     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
541     static std::map<SPDesktop*, GtkActionGroup*> groups;
542     GtkActionGroup* mainActions = 0;
543     if ( groups.find(desktop) != groups.end() ) {
544         mainActions = groups[desktop];
545     }
547     if ( !mainActions ) {
548         mainActions = gtk_action_group_new("main");
549         groups[desktop] = mainActions;
550     }
552     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
553         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
554         if ( verb ) {
555             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
556                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
557                 gtk_action_group_add_action( mainActions, act );
558             }
559         }
560     }
562     return mainActions;
566 GtkWidget *
567 sp_tool_toolbox_new()
569     GtkTooltips *tt = gtk_tooltips_new();
570     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
572     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
573     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
575     gtk_widget_set_sensitive(tb, FALSE);
577     GtkWidget *hb = gtk_handle_box_new();
578     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
579     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
580     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
582     gtk_container_add(GTK_CONTAINER(hb), tb);
583     gtk_widget_show(GTK_WIDGET(tb));
585     sigc::connection* conn = new sigc::connection;
586     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
588     return hb;
591 static void
592 aux_toolbox_attached(GtkHandleBox *toolbox, GtkWidget *child)
594     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
595     gtk_widget_queue_resize(child);
598 static void
599 aux_toolbox_detached(GtkHandleBox *toolbox, GtkWidget *child)
601     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
602     gtk_widget_queue_resize(child);
605 GtkWidget *
606 sp_aux_toolbox_new()
608     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
610     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
611     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
612     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
613     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
614     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
616     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
617     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
619     gtk_widget_set_sensitive(tb, FALSE);
621     GtkWidget *hb = gtk_handle_box_new();
622     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
623     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
624     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
626     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
627     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
629     gtk_container_add(GTK_CONTAINER(hb), tb);
630     gtk_widget_show(GTK_WIDGET(tb));
632     sigc::connection* conn = new sigc::connection;
633     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
635     return hb;
638 //####################################
639 //# Commands Bar
640 //####################################
642 GtkWidget *
643 sp_commands_toolbox_new()
645     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
647     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
648     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
649     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
650     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
651     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
653     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
654     gtk_widget_set_sensitive(tb, FALSE);
656     GtkWidget *hb = gtk_handle_box_new();
657     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
658     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
659     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
661     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
662     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
664     gtk_container_add(GTK_CONTAINER(hb), tb);
665     gtk_widget_show(GTK_WIDGET(tb));
667     sigc::connection* conn = new sigc::connection;
668     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
670     return hb;
674 //####################################
675 //# node editing callbacks
676 //####################################
678 /**
679  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
680  */
681 static ShapeEditor *get_current_shape_editor()
683     if (!SP_ACTIVE_DESKTOP) {
684         return NULL;
685     }
687     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
689     if (!SP_IS_NODE_CONTEXT(event_context)) {
690         return NULL;
691     }
693     return SP_NODE_CONTEXT(event_context)->shape_editor;
697 void
698 sp_node_path_edit_add(void)
700     ShapeEditor *shape_editor = get_current_shape_editor();
701     if (shape_editor) shape_editor->add_node();
704 void
705 sp_node_path_edit_delete(void)
707     ShapeEditor *shape_editor = get_current_shape_editor();
708     if (shape_editor) shape_editor->delete_nodes();
711 void
712 sp_node_path_edit_delete_segment(void)
714     ShapeEditor *shape_editor = get_current_shape_editor();
715     if (shape_editor) shape_editor->delete_segment();
718 void
719 sp_node_path_edit_break(void)
721     ShapeEditor *shape_editor = get_current_shape_editor();
722     if (shape_editor) shape_editor->break_at_nodes();
725 void
726 sp_node_path_edit_join(void)
728     ShapeEditor *shape_editor = get_current_shape_editor();
729     if (shape_editor) shape_editor->join_nodes();
732 void
733 sp_node_path_edit_join_segment(void)
735     ShapeEditor *shape_editor = get_current_shape_editor();
736     if (shape_editor) shape_editor->join_segments();
739 void
740 sp_node_path_edit_toline(void)
742     ShapeEditor *shape_editor = get_current_shape_editor();
743     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
746 void
747 sp_node_path_edit_tocurve(void)
749     ShapeEditor *shape_editor = get_current_shape_editor();
750     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
753 void
754 sp_node_path_edit_cusp(void)
756     ShapeEditor *shape_editor = get_current_shape_editor();
757     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
760 void
761 sp_node_path_edit_smooth(void)
763     ShapeEditor *shape_editor = get_current_shape_editor();
764     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
767 void
768 sp_node_path_edit_symmetrical(void)
770     ShapeEditor *shape_editor = get_current_shape_editor();
771     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
774 static void toggle_show_handles (GtkToggleAction *act, gpointer data) {
775     bool show = gtk_toggle_action_get_active( act );
776     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
777     ShapeEditor *shape_editor = get_current_shape_editor();
778     if (shape_editor) shape_editor->show_handles(show);
781 //################################
782 //##    Node Editing Toolbox    ##
783 //################################
785 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
787     {
788         InkAction* inky = ink_action_new( "NodeInsertAction",
789                                           _("Insert"),
790                                           _("Insert new nodes into selected segments"),
791                                           "node_insert",
792                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
793         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
794         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
795     }
797     {
798         InkAction* inky = ink_action_new( "NodeDeleteAction",
799                                           _("Delete"),
800                                           _("Delete selected nodes"),
801                                           "node_delete",
802                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
803         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
804         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
805     }
807     {
808         InkAction* inky = ink_action_new( "NodeJoinAction",
809                                           _("Join"),
810                                           _("Join selected endnodes"),
811                                           "node_join",
812                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
813         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
814         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
815     }
817     {
818         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
819                                           _("Join Segment"),
820                                           _("Join selected endnodes with a new segment"),
821                                           "node_join_segment",
822                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
823         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
824         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
825     }
827     {
828         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
829                                           _("Delete Segment"),
830                                           _("Split path between two non-endpoint nodes"),
831                                           "node_delete_segment",
832                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
833         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
834         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
835     }
837     {
838         InkAction* inky = ink_action_new( "NodeBreakAction",
839                                           _("Node Break"),
840                                           _("Break path at selected nodes"),
841                                           "node_break",
842                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
843         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
844         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
845     }
847     {
848         InkAction* inky = ink_action_new( "NodeCuspAction",
849                                           _("Node Cusp"),
850                                           _("Make selected nodes corner"),
851                                           "node_cusp",
852                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
853         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
854         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
855     }
857     {
858         InkAction* inky = ink_action_new( "NodeSmoothAction",
859                                           _("Node Smooth"),
860                                           _("Make selected nodes smooth"),
861                                           "node_smooth",
862                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
863         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
864         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
865     }
867     {
868         InkAction* inky = ink_action_new( "NodeSymmetricAction",
869                                           _("Node Symmetric"),
870                                           _("Make selected nodes symmetric"),
871                                           "node_symmetric",
872                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
873         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
874         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
875     }
877     {
878         InkAction* inky = ink_action_new( "NodeLineAction",
879                                           _("Node Line"),
880                                           _("Make selected segments lines"),
881                                           "node_line",
882                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
883         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
884         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
885     }
887     {
888         InkAction* inky = ink_action_new( "NodeCurveAction",
889                                           _("Node Curve"),
890                                           _("Make selected segments curves"),
891                                           "node_curve",
892                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
893         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
894         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
895     }
897     {
898         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
899                                                       _("Show Handles"),
900                                                       _("Show the Bezier handles of selected nodes"),
901                                                       "nodes_show_handles",
902                                                       Inkscape::ICON_SIZE_DECORATION );
903         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
904         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
905         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
906     }
908 } // end of sp_node_toolbox_prep()
911 //########################
912 //##    Zoom Toolbox    ##
913 //########################
915 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
917     // no custom GtkAction setup needed
918 } // end of sp_zoom_toolbox_prep()
920 void
921 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
923     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")));
927 void
928 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
930     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")));
933 void
934 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
936     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")));
939 static void
940 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
942     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
943     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
945     if (old_desktop) {
946         GList *children, *iter;
948         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
949         for ( iter = children ; iter ; iter = iter->next ) {
950             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
951         }
952         g_list_free(children);
953     }
955     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
957     if (desktop) {
958         gtk_widget_set_sensitive(toolbox, TRUE);
959         setup_func(toolbox, desktop);
960         update_func(desktop, desktop->event_context, toolbox);
961         *conn = desktop->connectEventContextChanged
962             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
963     } else {
964         gtk_widget_set_sensitive(toolbox, FALSE);
965     }
967 } // end of toolbox_set_desktop()
970 static void
971 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
973     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
974     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
975     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
976         // "toolbox.tools" was not set. Fallback to older value
977         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
979         // Copy the setting forwards
980         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
981     }
982     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
984     for (int i = 0 ; tools[i].type_name ; i++ ) {
985         GtkWidget *button =
986             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
987                                                               SP_BUTTON_TYPE_TOGGLE,
988                                                               Inkscape::Verb::get(tools[i].verb),
989                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
990                                                               desktop,
991                                                               tooltips );
993         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
994                            (gpointer)button );
995     }
999 static void
1000 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
1002     gchar const *const tname = ( eventcontext
1003                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1004                                  : NULL );
1005     for (int i = 0 ; tools[i].type_name ; i++ ) {
1006         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1007         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1008     }
1011 static void
1012 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1014     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1015     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1016     GtkUIManager* mgr = gtk_ui_manager_new();
1017     GError* errVal = 0;
1018     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1019     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1021     std::map<std::string, GtkWidget*> dataHolders;
1023     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1024         if ( aux_toolboxes[i].prep_func ) {
1025             // converted to GtkActions and UIManager
1027             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1028             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1029             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1030             dataHolders[aux_toolboxes[i].type_name] = kludge;
1031             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1032         } else {
1034             GtkWidget *sub_toolbox = 0;
1035             if (aux_toolboxes[i].create_func == NULL)
1036                 sub_toolbox = sp_empty_toolbox_new(desktop);
1037             else {
1038                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1039             }
1041             gtk_size_group_add_widget( grouper, sub_toolbox );
1043             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1044             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1046         }
1047     }
1049     // Second pass to create toolbars *after* all GtkActions are created
1050     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1051         if ( aux_toolboxes[i].prep_func ) {
1052             // converted to GtkActions and UIManager
1054             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1056             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1057             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1059             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1060             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1061             g_free( tmp );
1062             tmp = 0;
1064             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1065             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1066             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1067                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1068             }
1069             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1072             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1074             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1075                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1076                 swatch->setDesktop( desktop );
1077                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1078                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1079                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1080                 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 );
1081             }
1083             gtk_widget_show_all( holder );
1084             sp_set_font_size_smaller( holder );
1086             gtk_size_group_add_widget( grouper, holder );
1088             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1089             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1090         }
1091     }
1093     g_object_unref( G_OBJECT(grouper) );
1096 static void
1097 update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1099     gchar const *tname = ( eventcontext
1100                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1101                            : NULL );
1102     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1103         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1104         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1105             gtk_widget_show_all(sub_toolbox);
1106             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1107         } else {
1108             gtk_widget_hide(sub_toolbox);
1109         }
1110     }
1113 static void
1114 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1116     gchar const * descr =
1117         "<ui>"
1118         "  <toolbar name='CommandsToolbar'>"
1119         "    <toolitem action='FileNew' />"
1120         "    <toolitem action='FileOpen' />"
1121         "    <toolitem action='FileSave' />"
1122         "    <toolitem action='FilePrint' />"
1123         "    <separator />"
1124         "    <toolitem action='FileImport' />"
1125         "    <toolitem action='FileExport' />"
1126         "    <separator />"
1127         "    <toolitem action='EditUndo' />"
1128         "    <toolitem action='EditRedo' />"
1129         "    <separator />"
1130         "    <toolitem action='EditCopy' />"
1131         "    <toolitem action='EditCut' />"
1132         "    <toolitem action='EditPaste' />"
1133         "    <separator />"
1134         "    <toolitem action='ZoomSelection' />"
1135         "    <toolitem action='ZoomDrawing' />"
1136         "    <toolitem action='ZoomPage' />"
1137         "    <separator />"
1138         "    <toolitem action='EditDuplicate' />"
1139         "    <toolitem action='EditClone' />"
1140         "    <toolitem action='EditUnlinkClone' />"
1141         "    <separator />"
1142         "    <toolitem action='SelectionGroup' />"
1143         "    <toolitem action='SelectionUnGroup' />"
1144         "    <separator />"
1145         "    <toolitem action='DialogFillStroke' />"
1146         "    <toolitem action='DialogText' />"
1147         "    <toolitem action='DialogXMLEditor' />"
1148         "    <toolitem action='DialogAlignDistribute' />"
1149         "    <separator />"
1150         "    <toolitem action='DialogPreferences' />"
1151         "    <toolitem action='DialogDocumentProperties' />"
1152         "  </toolbar>"
1153         "</ui>";
1154     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1157     GtkUIManager* mgr = gtk_ui_manager_new();
1158     GError* errVal = 0;
1160     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1161     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1163     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1164     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1165         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1166     }
1167     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1168     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1169     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1172     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1175 static void
1176 update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1180 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1182     gtk_widget_show(toolbox_toplevel);
1183     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1185     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1186     if (!shown_toolbox) {
1187         return;
1188     }
1189     gtk_widget_show(toolbox);
1191     // need to show the spacer, or the padding will be off
1192     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1193     gtk_widget_show(spacer);
1195     gtk_widget_show_all(shown_toolbox);
1198 void
1199 aux_toolbox_space(GtkWidget *tb, gint space)
1201     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1204 static GtkWidget *
1205 sp_empty_toolbox_new(SPDesktop *desktop)
1207     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1208     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1209     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1211     gtk_widget_show_all(tbl);
1212     sp_set_font_size_smaller (tbl);
1214     return tbl;
1217 // helper UI functions
1219 GtkWidget *
1220 sp_tb_spinbutton(
1221     gchar *label, gchar const *tooltip,
1222     gchar const *path, gchar const *data, gdouble def,
1223     GtkWidget *us,
1224     GtkWidget *tbl,
1225     gboolean altx, gchar const *altx_mark,
1226     gdouble lower, gdouble upper, gdouble step, gdouble page,
1227     void (*callback)(GtkAdjustment *, GtkWidget *),
1228     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1230     GtkTooltips *tt = gtk_tooltips_new();
1232     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1234     GtkWidget *l = gtk_label_new(label);
1235     gtk_widget_show(l);
1236     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1237     gtk_container_add(GTK_CONTAINER(hb), l);
1239     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1240                                       lower, upper, step, page, page);
1241     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1242     if (us)
1243         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1245     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1246     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1247     if (altx)
1248         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1249     gtk_widget_set_size_request(sb,
1250                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1251                                 AUX_SPINBUTTON_HEIGHT);
1252     gtk_widget_show(sb);
1253     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1254     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1255     gtk_container_add(GTK_CONTAINER(hb), sb);
1256     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1258     return hb;
1261 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1262                                                        gchar const *label, gchar const *tooltip,
1263                                                        gchar const *path, gchar const *data, gdouble def,
1264                                                        GtkWidget *focusTarget,
1265                                                        GtkWidget *us,
1266                                                        GObject *dataKludge,
1267                                                        gboolean altx, gchar const *altx_mark,
1268                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1269                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1270                                                        void (*callback)(GtkAdjustment *, GObject *),
1271                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1273     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
1274                                                              lower, upper, step, page, page ) );
1275     if (us) {
1276         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1277     }
1279     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1281     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1283     if ( (descrCount > 0) && descrLabels && descrValues ) {
1284         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1285     }
1287     if ( focusTarget ) {
1288         ege_adjustment_action_set_focuswidget( act, focusTarget );
1289     }
1291     if ( altx && altx_mark ) {
1292         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1293     }
1295     if ( dataKludge ) {
1296         g_object_set_data( dataKludge, data, adj );
1297     }
1299     // Using a cast just to make sure we pass in the right kind of function pointer
1300     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1302     return act;
1306 #define MODE_LABEL_WIDTH 70
1308 //########################
1309 //##       Star         ##
1310 //########################
1312 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1314     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1316     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1317         // do not remember prefs if this call is initiated by an undo change, because undoing object
1318         // creation sets bogus values to its attributes before it is deleted
1319         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1320     }
1322     // quit if run by the attr_changed listener
1323     if (g_object_get_data( dataKludge, "freeze" )) {
1324         return;
1325     }
1327     // in turn, prevent listener from responding
1328     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1330     bool modmade = false;
1332     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1333     GSList const *items = selection->itemList();
1334     for (; items != NULL; items = items->next) {
1335         if (SP_IS_STAR((SPItem *) items->data)) {
1336             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1337             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1338             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1339                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1340                                     + M_PI / (gint)adj->value));
1341             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1342             modmade = true;
1343         }
1344     }
1345     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1346                                    _("Star: Change number of corners"));
1348     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1351 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1353     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1355     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1356         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1357     }
1359     // quit if run by the attr_changed listener
1360     if (g_object_get_data( dataKludge, "freeze" )) {
1361         return;
1362     }
1364     // in turn, prevent listener from responding
1365     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1367     bool modmade = false;
1368     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1369     GSList const *items = selection->itemList();
1370     for (; items != NULL; items = items->next) {
1371         if (SP_IS_STAR((SPItem *) items->data)) {
1372             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1374             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1375             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1376             if (r2 < r1) {
1377                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1378             } else {
1379                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1380             }
1382             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1383             modmade = true;
1384         }
1385     }
1387     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1388                                    _("Star: Change spoke ratio"));
1390     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1393 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1395     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1396     bool flat = ege_select_one_action_get_active( act ) == 0;
1398     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1399         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1400                                     flat ? "true" : "false" );
1401     }
1403     // quit if run by the attr_changed listener
1404     if (g_object_get_data( dataKludge, "freeze" )) {
1405         return;
1406     }
1408     // in turn, prevent listener from responding
1409     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1411     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1412     GSList const *items = selection->itemList();
1413     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1414     bool modmade = false;
1416     if ( prop_action ) {
1417         gtk_action_set_sensitive( prop_action, !flat );
1418     }
1420     for (; items != NULL; items = items->next) {
1421         if (SP_IS_STAR((SPItem *) items->data)) {
1422             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1423             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1424             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1425             modmade = true;
1426         }
1427     }
1429     if (modmade) {
1430         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1431                          flat ? _("Make polygon") : _("Make star"));
1432     }
1434     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1437 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1439     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1441     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1442         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1443     }
1445     // quit if run by the attr_changed listener
1446     if (g_object_get_data( dataKludge, "freeze" )) {
1447         return;
1448     }
1450     // in turn, prevent listener from responding
1451     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1453     bool modmade = false;
1455     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1456     GSList const *items = selection->itemList();
1457     for (; items != NULL; items = items->next) {
1458         if (SP_IS_STAR((SPItem *) items->data)) {
1459             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1460             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1461             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1462             modmade = true;
1463         }
1464     }
1465     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1466                                    _("Star: Change rounding"));
1468     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1471 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1473     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1475     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1476         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1477     }
1479     // quit if run by the attr_changed listener
1480     if (g_object_get_data( dataKludge, "freeze" )) {
1481         return;
1482     }
1484     // in turn, prevent listener from responding
1485     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1487     bool modmade = false;
1489     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1490     GSList const *items = selection->itemList();
1491     for (; items != NULL; items = items->next) {
1492         if (SP_IS_STAR((SPItem *) items->data)) {
1493             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1494             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1495             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1496             modmade = true;
1497         }
1498     }
1499     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1500                                    _("Star: Change randomization"));
1502     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1506 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1507                                        gchar const *old_value, gchar const *new_value,
1508                                        bool is_interactive, gpointer data)
1510     GtkWidget *tbl = GTK_WIDGET(data);
1512     // quit if run by the _changed callbacks
1513     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1514         return;
1515     }
1517     // in turn, prevent callbacks from responding
1518     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1520     GtkAdjustment *adj = 0;
1522     if (!strcmp(name, "inkscape:randomized")) {
1523         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1524         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1525     } else if (!strcmp(name, "inkscape:rounded")) {
1526         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1527         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1528     } else if (!strcmp(name, "inkscape:flatsided")) {
1529         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1530         char const *flatsides = repr->attribute("inkscape:flatsided");
1531         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1532         if ( flatsides && !strcmp(flatsides,"false") ) {
1533             ege_select_one_action_set_active( flat_action, 1 );
1534             gtk_action_set_sensitive( prop_action, TRUE );
1535         } else {
1536             ege_select_one_action_set_active( flat_action, 0 );
1537             gtk_action_set_sensitive( prop_action, FALSE );
1538         }
1539     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1540         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1541         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1542         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1543         if (r2 < r1) {
1544             gtk_adjustment_set_value(adj, r2/r1);
1545         } else {
1546             gtk_adjustment_set_value(adj, r1/r2);
1547         }
1548     } else if (!strcmp(name, "sodipodi:sides")) {
1549         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1550         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1551     }
1553     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1557 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1559     NULL, /* child_added */
1560     NULL, /* child_removed */
1561     star_tb_event_attr_changed,
1562     NULL, /* content_changed */
1563     NULL  /* order_changed */
1564 };
1567 /**
1568  *  \param selection Should not be NULL.
1569  */
1570 static void
1571 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1573     int n_selected = 0;
1574     Inkscape::XML::Node *repr = NULL;
1576     purge_repr_listener( tbl, tbl );
1578     for (GSList const *items = selection->itemList();
1579          items != NULL;
1580          items = items->next)
1581     {
1582         if (SP_IS_STAR((SPItem *) items->data)) {
1583             n_selected++;
1584             repr = SP_OBJECT_REPR((SPItem *) items->data);
1585         }
1586     }
1588     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1590     if (n_selected == 0) {
1591         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1592     } else if (n_selected == 1) {
1593         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1595         if (repr) {
1596             g_object_set_data( tbl, "repr", repr );
1597             Inkscape::GC::anchor(repr);
1598             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1599             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1600         }
1601     } else {
1602         // FIXME: implement averaging of all parameters for multiple selected stars
1603         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1604         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1605     }
1609 static void sp_stb_defaults( GtkWidget *widget, GObject *dataKludge )
1611     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1612     // callbacks to lump all the changes for all selected objects in one undo step
1614     GtkAdjustment *adj = 0;
1616     // fixme: make settable in prefs!
1617     gint mag = 5;
1618     gdouble prop = 0.5;
1619     gboolean flat = FALSE;
1620     gdouble randomized = 0;
1621     gdouble rounded = 0;
1623     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1624     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1626     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1627     gtk_action_set_sensitive( sb2, !flat );
1629     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1630     gtk_adjustment_set_value(adj, mag);
1631     gtk_adjustment_value_changed(adj);
1633     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1634     gtk_adjustment_set_value(adj, prop);
1635     gtk_adjustment_value_changed(adj);
1637     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1638     gtk_adjustment_set_value(adj, rounded);
1639     gtk_adjustment_value_changed(adj);
1641     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1642     gtk_adjustment_set_value(adj, randomized);
1643     gtk_adjustment_value_changed(adj);
1647 void
1648 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1650     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1651     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1652     GtkWidget *l = gtk_label_new(NULL);
1653     gtk_label_set_markup(GTK_LABEL(l), title);
1654     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1655     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1656     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1660 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1662     {
1663         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1664         ege_output_action_set_use_markup( act, TRUE );
1665         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1666         g_object_set_data( holder, "mode_action", act );
1667     }
1669     {
1670         //EgeAdjustmentAction* calligraphy_angle = 0;
1671         EgeAdjustmentAction* eact = 0;
1672         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1673         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1675         /* Flatsided checkbox */
1676         {
1677             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1679             GtkTreeIter iter;
1680             gtk_list_store_append( model, &iter );
1681             gtk_list_store_set( model, &iter,
1682                                 0, _("Polygon"),
1683                                 1, _("Regular polygon (with one handle) instead of a star"),
1684                                 2, "star_flat",
1685                                 -1 );
1687             gtk_list_store_append( model, &iter );
1688             gtk_list_store_set( model, &iter,
1689                                 0, _("Star"),
1690                                 1, _("Star instead of a regular polygon (with one handle)"),
1691                                 2, "star_angled",
1692                                 -1 );
1694             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
1695             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1696             g_object_set_data( holder, "flat_action", act );
1698             ege_select_one_action_set_appearance( act, "full" );
1699             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1700             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1701             ege_select_one_action_set_icon_column( act, 2 );
1702             ege_select_one_action_set_tooltip_column( act, 1  );
1704             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1705             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1706         }
1708         /* Magnitude */
1709         {
1710         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1711         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1712         eact = create_adjustment_action( "MagnitudeAction",
1713                                          _("Corners:"), _("Number of corners of a polygon or star"),
1714                                          "tools.shapes.star", "magnitude", 3,
1715                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1716                                          3, 1024, 1, 5,
1717                                          labels, values, G_N_ELEMENTS(labels),
1718                                          sp_stb_magnitude_value_changed,
1719                                          1.0, 0 );
1720         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1721         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1722         }
1724         /* Spoke ratio */
1725         {
1726         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1727         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1728         eact = create_adjustment_action( "SpokeAction",
1729                                          _("Spoke ratio:"),
1730                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1731                                          // Base radius is the same for the closest handle.
1732                                          _("Base radius to tip radius ratio"),
1733                                          "tools.shapes.star", "proportion", 0.5,
1734                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1735                                          0.01, 1.0, 0.01, 0.1,
1736                                          labels, values, G_N_ELEMENTS(labels),
1737                                          sp_stb_proportion_value_changed );
1738         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1739         g_object_set_data( holder, "prop_action", eact );
1740         }
1742         if ( !isFlatSided ) {
1743             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1744         } else {
1745             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1746         }
1748         /* Roundedness */
1749         {
1750         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1751         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1752         eact = create_adjustment_action( "RoundednessAction",
1753                                          _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1754                                          "tools.shapes.star", "rounded", 0.0,
1755                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1756                                          -10.0, 10.0, 0.01, 0.1,
1757                                          labels, values, G_N_ELEMENTS(labels),
1758                                          sp_stb_rounded_value_changed );
1759         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1760         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1761         }
1763         /* Randomization */
1764         {
1765         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1766         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1767         eact = create_adjustment_action( "RandomizationAction",
1768                                          _("Randomized:"), _("Scatter randomly the corners and angles"),
1769                                          "tools.shapes.star", "randomized", 0.0,
1770                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1771                                          -10.0, 10.0, 0.001, 0.01,
1772                                          labels, values, G_N_ELEMENTS(labels),
1773                                          sp_stb_randomized_value_changed, 0.1, 3 );
1774         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1775         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1776         }
1777     }
1779     {
1780         /* Reset */
1781         {
1782             GtkAction* act = gtk_action_new( "StarResetAction",
1783                                              _("Defaults"),
1784                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1785                                              GTK_STOCK_CLEAR );
1786             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1787             gtk_action_group_add_action( mainActions, act );
1788             gtk_action_set_sensitive( act, TRUE );
1789         }
1790     }
1792     sigc::connection *connection = new sigc::connection(
1793         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1794         );
1795     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1796     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1800 //########################
1801 //##       Rect         ##
1802 //########################
1804 static void sp_rtb_sensitivize( GObject *tbl )
1806     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1807     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1808     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1810     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1811         gtk_action_set_sensitive( not_rounded, FALSE );
1812     } else {
1813         gtk_action_set_sensitive( not_rounded, TRUE );
1814     }
1818 static void
1819 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1820                           void (*setter)(SPRect *, gdouble))
1822     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1824     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1825     SPUnit const *unit = tracker->getActiveUnit();
1827     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1828         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1829     }
1831     // quit if run by the attr_changed listener
1832     if (g_object_get_data( tbl, "freeze" )) {
1833         return;
1834     }
1836     // in turn, prevent listener from responding
1837     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1839     bool modmade = false;
1840     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1841     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1842         if (SP_IS_RECT(items->data)) {
1843             if (adj->value != 0) {
1844                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
1845             } else {
1846                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
1847             }
1848             modmade = true;
1849         }
1850     }
1852     sp_rtb_sensitivize( tbl );
1854     if (modmade) {
1855         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
1856                                    _("Change rectangle"));
1857     }
1859     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1862 static void
1863 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
1865     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
1868 static void
1869 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
1871     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
1874 static void
1875 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
1877     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
1880 static void
1881 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
1883     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
1888 static void
1889 sp_rtb_defaults( GtkWidget *widget, GObject *obj)
1891     GtkAdjustment *adj = 0;
1893     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
1894     gtk_adjustment_set_value(adj, 0.0);
1895     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
1896     gtk_adjustment_value_changed(adj);
1898     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
1899     gtk_adjustment_set_value(adj, 0.0);
1900     gtk_adjustment_value_changed(adj);
1902     sp_rtb_sensitivize( obj );
1905 static void rect_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1906                                        gchar const *old_value, gchar const *new_value,
1907                                        bool is_interactive, gpointer data)
1909     GObject *tbl = G_OBJECT(data);
1911     // quit if run by the _changed callbacks
1912     if (g_object_get_data( tbl, "freeze" )) {
1913         return;
1914     }
1916     // in turn, prevent callbacks from responding
1917     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
1919     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1920     SPUnit const *unit = tracker->getActiveUnit();
1922     gpointer item = g_object_get_data( tbl, "item" );
1923     if (item && SP_IS_RECT(item)) {
1924         {
1925             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
1926             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
1927             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
1928         }
1930         {
1931             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
1932             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
1933             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
1934         }
1936         {
1937             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
1938             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
1939             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
1940         }
1942         {
1943             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
1944             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
1945             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
1946         }
1947     }
1949     sp_rtb_sensitivize( tbl );
1951     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1955 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
1956     NULL, /* child_added */
1957     NULL, /* child_removed */
1958     rect_tb_event_attr_changed,
1959     NULL, /* content_changed */
1960     NULL  /* order_changed */
1961 };
1963 /**
1964  *  \param selection should not be NULL.
1965  */
1966 static void
1967 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1969     int n_selected = 0;
1970     Inkscape::XML::Node *repr = NULL;
1971     SPItem *item = NULL;
1973     if ( g_object_get_data( tbl, "repr" ) ) {
1974         g_object_set_data( tbl, "item", NULL );
1975     }
1976     purge_repr_listener( tbl, tbl );
1978     for (GSList const *items = selection->itemList();
1979          items != NULL;
1980          items = items->next) {
1981         if (SP_IS_RECT((SPItem *) items->data)) {
1982             n_selected++;
1983             item = (SPItem *) items->data;
1984             repr = SP_OBJECT_REPR(item);
1985         }
1986     }
1988     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1990     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
1992     if (n_selected == 0) {
1993         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1995         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
1996         gtk_action_set_sensitive(w, FALSE);
1997         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
1998         gtk_action_set_sensitive(h, FALSE);
2000     } else if (n_selected == 1) {
2001         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2002         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2004         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2005         gtk_action_set_sensitive(w, TRUE);
2006         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2007         gtk_action_set_sensitive(h, TRUE);
2009         if (repr) {
2010             g_object_set_data( tbl, "repr", repr );
2011             g_object_set_data( tbl, "item", item );
2012             Inkscape::GC::anchor(repr);
2013             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2014             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2015         }
2016     } else {
2017         // FIXME: implement averaging of all parameters for multiple selected
2018         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2019         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2020         sp_rtb_sensitivize( tbl );
2021     }
2025 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2027     EgeAdjustmentAction* eact = 0;
2029     {
2030         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2031         ege_output_action_set_use_markup( act, TRUE );
2032         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2033         g_object_set_data( holder, "mode_action", act );
2034     }
2036     // rx/ry units menu: create
2037     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2038     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2039     // fixme: add % meaning per cent of the width/height
2040     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2041     g_object_set_data( holder, "tracker", tracker );
2043     /* W */
2044     {
2045         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2046         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2047         eact = create_adjustment_action( "RectWidthAction",
2048                                          _("W:"), _("Width of rectangle"),
2049                                          "tools.shapes.rect", "width", 0,
2050                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2051                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2052                                          labels, values, G_N_ELEMENTS(labels),
2053                                          sp_rtb_width_value_changed );
2054         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2055         g_object_set_data( holder, "width_action", eact );
2056         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2057         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2058     }
2060     /* H */
2061     {
2062         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2063         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2064         eact = create_adjustment_action( "RectHeightAction",
2065                                          _("H:"), _("Height of rectangle"),
2066                                          "tools.shapes.rect", "height", 0,
2067                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2068                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2069                                          labels, values, G_N_ELEMENTS(labels),
2070                                          sp_rtb_height_value_changed );
2071         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2072         g_object_set_data( holder, "height_action", eact );
2073         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2074         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2075     }
2077     /* rx */
2078     {
2079         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2080         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2081         eact = create_adjustment_action( "RadiusXAction",
2082                                          _("Rx:"), _("Horizontal radius of rounded corners"),
2083                                          "tools.shapes.rect", "rx", 0,
2084                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2085                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2086                                          labels, values, G_N_ELEMENTS(labels),
2087                                          sp_rtb_rx_value_changed);
2088         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2089         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2090     }
2092     /* ry */
2093     {
2094         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2095         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2096         eact = create_adjustment_action( "RadiusYAction",
2097                                          _("Ry:"), _("Vertical radius of rounded corners"),
2098                                          "tools.shapes.rect", "ry", 0,
2099                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2100                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2101                                          labels, values, G_N_ELEMENTS(labels),
2102                                          sp_rtb_ry_value_changed);
2103         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2104         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2105     }
2107     // add the units menu
2108     {
2109         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), _("") );
2110         gtk_action_group_add_action( mainActions, act );
2111     }
2113     /* Reset */
2114     {
2115         InkAction* inky = ink_action_new( "RectResetAction",
2116                                           _("Not rounded"),
2117                                           _("Make corners sharp"),
2118                                           "squared_corner",
2119                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2120         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2121         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2122         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2123         g_object_set_data( holder, "not_rounded", inky );
2124     }
2126     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2127     sp_rtb_sensitivize( holder );
2129     sigc::connection *connection = new sigc::connection(
2130         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2131         );
2132     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2133     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2136 //########################
2137 //##       3D Box       ##
2138 //########################
2140 static void sp_3dbox_toggle_vp_changed( GtkToggleAction *act, gpointer data )
2142     SPDocument *document = sp_desktop_document (inkscape_active_desktop ());
2143     Box3D::Axis axis = (Box3D::Axis) GPOINTER_TO_INT(data);
2145     if (document->current_perspective) {
2146         document->current_perspective->toggle_boxes (axis);
2147     }
2148     
2151 static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2153     SPDocument *document = sp_desktop_document (desktop);
2154     bool toggled = false;
2155     /* toggle VP in X direction */
2156     {
2157     InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXAction",
2158                                                   _("Toggle VP in X direction"),
2159                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2160                                                   "toggle_vp_x",
2161                                                   Inkscape::ICON_SIZE_DECORATION );
2162     gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2163     if (document->current_perspective) {
2164         toggled = !document->current_perspective->get_vanishing_point(Box3D::X)->is_finite();
2165     }
2166     gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
2167     /* we connect the signal after setting the state to avoid switching the state again */
2168     g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), GINT_TO_POINTER(Box3D::X));
2169     }
2171     /* toggle VP in Y direction */
2172     {
2173     InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYAction",
2174                                                   _("Toggle VP in Y direction"),
2175                                                   _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2176                                                   "toggle_vp_y",
2177                                                   Inkscape::ICON_SIZE_DECORATION );
2178     gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2179     if (document->current_perspective) {
2180         toggled = !document->current_perspective->get_vanishing_point(Box3D::Y)->is_finite();
2181     }
2182     gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
2183     /* we connect the signal after setting the state to avoid switching the state again */
2184     g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), GINT_TO_POINTER(Box3D::Y));
2185     }
2187     /* toggle VP in Z direction */
2188     {
2189     InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZAction",
2190                                                   _("Toggle VP in Z direction"),
2191                                                   _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2192                                                   "toggle_vp_z",
2193                                                   Inkscape::ICON_SIZE_DECORATION );
2194     gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2195     if (document->current_perspective) {
2196         toggled = !document->current_perspective->get_vanishing_point(Box3D::Z)->is_finite();
2197     }
2198     /* we connect the signal after setting the state to avoid switching the state again */
2199     gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
2200     g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), GINT_TO_POINTER(Box3D::Z));
2201     }
2204 //########################
2205 //##       Spiral       ##
2206 //########################
2208 static void
2209 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2211     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2213     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2214         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2215     }
2217     // quit if run by the attr_changed listener
2218     if (g_object_get_data( tbl, "freeze" )) {
2219         return;
2220     }
2222     // in turn, prevent listener from responding
2223     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2225     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2227     bool modmade = false;
2228     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2229          items != NULL;
2230          items = items->next)
2231     {
2232         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2233             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2234             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2235             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2236             modmade = true;
2237         }
2238     }
2240     g_free(namespaced_name);
2242     if (modmade) {
2243         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2244                                    _("Change spiral"));
2245     }
2247     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2250 static void
2251 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2253     sp_spl_tb_value_changed(adj, tbl, "revolution");
2256 static void
2257 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2259     sp_spl_tb_value_changed(adj, tbl, "expansion");
2262 static void
2263 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2265     sp_spl_tb_value_changed(adj, tbl, "t0");
2268 static void
2269 sp_spl_tb_defaults(GtkWidget *widget, GtkObject *obj)
2271     GtkWidget *tbl = GTK_WIDGET(obj);
2273     GtkAdjustment *adj;
2275     // fixme: make settable
2276     gdouble rev = 5;
2277     gdouble exp = 1.0;
2278     gdouble t0 = 0.0;
2280     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2281     gtk_adjustment_set_value(adj, rev);
2282     gtk_adjustment_value_changed(adj);
2284     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2285     gtk_adjustment_set_value(adj, exp);
2286     gtk_adjustment_value_changed(adj);
2288     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2289     gtk_adjustment_set_value(adj, t0);
2290     gtk_adjustment_value_changed(adj);
2292     spinbutton_defocus(GTK_OBJECT(tbl));
2296 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2297                                          gchar const *old_value, gchar const *new_value,
2298                                          bool is_interactive, gpointer data)
2300     GtkWidget *tbl = GTK_WIDGET(data);
2302     // quit if run by the _changed callbacks
2303     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2304         return;
2305     }
2307     // in turn, prevent callbacks from responding
2308     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2310     GtkAdjustment *adj;
2311     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2312     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2314     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2315     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2317     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2318     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2320     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2324 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2325     NULL, /* child_added */
2326     NULL, /* child_removed */
2327     spiral_tb_event_attr_changed,
2328     NULL, /* content_changed */
2329     NULL  /* order_changed */
2330 };
2332 static void
2333 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2335     int n_selected = 0;
2336     Inkscape::XML::Node *repr = NULL;
2338     purge_repr_listener( tbl, tbl );
2340     for (GSList const *items = selection->itemList();
2341          items != NULL;
2342          items = items->next)
2343     {
2344         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2345             n_selected++;
2346             repr = SP_OBJECT_REPR((SPItem *) items->data);
2347         }
2348     }
2350     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2352     if (n_selected == 0) {
2353         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2354     } else if (n_selected == 1) {
2355         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2357         if (repr) {
2358             g_object_set_data( tbl, "repr", repr );
2359             Inkscape::GC::anchor(repr);
2360             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2361             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2362         }
2363     } else {
2364         // FIXME: implement averaging of all parameters for multiple selected
2365         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2366         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2367     }
2371 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2373     EgeAdjustmentAction* eact = 0;
2375     {
2376         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2377         ege_output_action_set_use_markup( act, TRUE );
2378         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2379         g_object_set_data( holder, "mode_action", act );
2380     }
2382     /* Revolution */
2383     {
2384         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2385         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2386         eact = create_adjustment_action( "SpiralRevolutionAction",
2387                                          _("Turns:"), _("Number of revolutions"),
2388                                          "tools.shapes.spiral", "revolution", 3.0,
2389                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2390                                          0.01, 1024.0, 0.1, 1.0,
2391                                          labels, values, G_N_ELEMENTS(labels),
2392                                          sp_spl_tb_revolution_value_changed, 1, 2);
2393         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2394     }
2396     /* Expansion */
2397     {
2398         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2399         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2400         eact = create_adjustment_action( "SpiralExpansionAction",
2401                                          _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2402                                          "tools.shapes.spiral", "expansion", 1.0,
2403                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2404                                          0.0, 1000.0, 0.01, 1.0,
2405                                          labels, values, G_N_ELEMENTS(labels),
2406                                          sp_spl_tb_expansion_value_changed);
2407         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2408     }
2410     /* T0 */
2411     {
2412         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2413         gdouble values[] = {0, 0.5, 0.9};
2414         eact = create_adjustment_action( "SpiralT0Action",
2415                                          _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2416                                          "tools.shapes.spiral", "t0", 0.0,
2417                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2418                                          0.0, 0.999, 0.01, 1.0,
2419                                          labels, values, G_N_ELEMENTS(labels),
2420                                          sp_spl_tb_t0_value_changed);
2421         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2422     }
2424     /* Reset */
2425     {
2426         InkAction* inky = ink_action_new( "SpiralResetAction",
2427                                           _("Defaults"),
2428                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2429                                           GTK_STOCK_CLEAR,
2430                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2431         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2432         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2433     }
2436     sigc::connection *connection = new sigc::connection(
2437         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2438         );
2439     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2440     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2443 //########################
2444 //##     Pen/Pencil    ##
2445 //########################
2448 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2450     // Put stuff here
2453 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2455     // Put stuff here
2458 //########################
2459 //##       Tweak        ##
2460 //########################
2462 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2464     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2467 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject *tbl )
2469     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2472 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer data )
2474     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2477 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2479     prefs_set_int_attribute("tools.tweak", "mode", ege_select_one_action_get_active( act ));
2482 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject *tbl )
2484     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2488 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2490     {
2491         /* Width */
2492         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2493         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2494         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2495                                                               _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2496                                                               "tools.tweak", "width", 15,
2497                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2498                                                               1, 100, 1.0, 10.0,
2499                                                               labels, values, G_N_ELEMENTS(labels),
2500                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2501         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2502         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2503     }
2506     {
2507         /* Force */
2508         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2509         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2510         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2511                                                               _("Force:"), _("The force of the tweak action"),
2512                                                               "tools.tweak", "force", 20,
2513                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2514                                                               1, 100, 1.0, 10.0,
2515                                                               labels, values, G_N_ELEMENTS(labels),
2516                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2517         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2518         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2519     }
2521     /* Mode */
2522     {
2523         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2525         GtkTreeIter iter;
2526         gtk_list_store_append( model, &iter );
2527         gtk_list_store_set( model, &iter,
2528                             0, _("Push mode"),
2529                             1, _("Switch to Push mode"),
2530                             2, "tweak_push_mode",
2531                             -1 );
2533         gtk_list_store_append( model, &iter );
2534         gtk_list_store_set( model, &iter,
2535                             0, _("Melt mode"),
2536                             1, _("Switch to Melt mode"),
2537                             2, "tweak_suck_mode",
2538                             -1 );
2540         gtk_list_store_append( model, &iter );
2541         gtk_list_store_set( model, &iter,
2542                             0, _("Inflate mode"),
2543                             1, _("Switch to Inflate mode"),
2544                             2, "tweak_inflate_mode",
2545                             -1 );
2547         gtk_list_store_append( model, &iter );
2548         gtk_list_store_set( model, &iter,
2549                             0, _("Roughen mode"),
2550                             1, _("Switch to Roughen mode"),
2551                             2, "tweak_roughen_mode",
2552                             -1 );
2554         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
2555         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2556         g_object_set_data( holder, "mode_action", act );
2558         ege_select_one_action_set_appearance( act, "full" );
2559         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2560         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2561         ege_select_one_action_set_icon_column( act, 2 );
2562         ege_select_one_action_set_tooltip_column( act, 1  );
2564         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2565         ege_select_one_action_set_active( act, mode );
2566         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
2568         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
2569     }
2571     {   /* Fidelity */
2572         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
2573         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
2574         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
2575                                                               _("Fidelity:"), _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
2576                                                               "tools.tweak", "fidelity", 50,
2577                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
2578                                                               1, 100, 1.0, 10.0,
2579                                                               labels, values, G_N_ELEMENTS(labels),
2580                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
2581         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2582         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2583     }
2586     /* Use Pressure button */
2587     {
2588         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
2589                                                       _("Pressure"),
2590                                                       _("Use the pressure of the input device to alter the width of the area"),
2591                                                       "use_pressure",
2592                                                       Inkscape::ICON_SIZE_DECORATION );
2593         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2594         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
2595         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
2596     }
2601 //########################
2602 //##     Calligraphy    ##
2603 //########################
2605 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
2607     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2610 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
2612     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2615 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
2617     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2620 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2622     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2625 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
2627     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
2630 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
2632     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
2635 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
2637     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
2640 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
2642     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
2645 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer data )
2647     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2650 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer data )
2652     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
2655 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
2657     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
2659     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
2662 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
2664     // FIXME: make defaults settable via Inkscape Options
2665     struct KeyValue {
2666         char const *key;
2667         double value;
2668     } const key_values[] = {
2669         {"mass", 0.02},
2670         {"wiggle", 0.0},
2671         {"angle", 30.0},
2672         {"width", 15},
2673         {"thinning", 0.1},
2674         {"tremor", 0.0},
2675         {"flatness", 0.9},
2676         {"cap_rounding", 0.0}
2677     };
2679     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
2680         KeyValue const &kv = key_values[i];
2681         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
2682         if ( adj ) {
2683             gtk_adjustment_set_value(adj, kv.value);
2684         }
2685     }
2689 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2691     {
2692         EgeAdjustmentAction* calligraphy_angle = 0;
2694         {
2695         /* Width */
2696         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
2697         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2698         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
2699                                          _("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
2700                                          "tools.calligraphic", "width", 15,
2701                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
2702                                          1, 100, 1.0, 10.0,
2703                                          labels, values, G_N_ELEMENTS(labels),
2704                                          sp_ddc_width_value_changed,  0.01, 0, 100 );
2705         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2706         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2707         }
2709         {
2710         /* Thinning */
2711             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
2712             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
2713         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
2714                                          _("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
2715                                          "tools.calligraphic", "thinning", 0.1,
2716                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2717                                          -1.0, 1.0, 0.01, 0.1,
2718                                          labels, values, G_N_ELEMENTS(labels),
2719                                          sp_ddc_velthin_value_changed, 0.01, 2);
2720         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2721         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2722         }
2724         {
2725         /* Angle */
2726         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
2727         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2728         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
2729                                          _("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
2730                                          "tools.calligraphic", "angle", 30,
2731                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
2732                                          -90.0, 90.0, 1.0, 10.0,
2733                                          labels, values, G_N_ELEMENTS(labels),
2734                                          sp_ddc_angle_value_changed, 1, 0 );
2735         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2736         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2737         calligraphy_angle = eact;
2738         }
2740         {
2741         /* Fixation */
2742             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
2743         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
2744         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
2745                                          _("Fixation:"), _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
2746                                          "tools.calligraphic", "flatness", 0.9,
2747                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2748                                          0.0, 1.0, 0.01, 0.1,
2749                                          labels, values, G_N_ELEMENTS(labels),
2750                                          sp_ddc_flatness_value_changed, 0.01, 2 );
2751         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2752         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2753         }
2755         {
2756         /* Cap Rounding */
2757             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
2758         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
2759         // TRANSLATORS: "cap" means "end" (both start and finish) here
2760         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
2761                                          _("Caps:"), _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
2762                                          "tools.calligraphic", "cap_rounding", 0.0,
2763                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2764                                          0.0, 5.0, 0.01, 0.1,
2765                                          labels, values, G_N_ELEMENTS(labels),
2766                                          sp_ddc_cap_rounding_value_changed, 0.01, 2 );
2767         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2768         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2769         }
2771         {
2772         /* Tremor */
2773             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
2774         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
2775         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
2776                                          _("Tremor:"), _("Increase to make strokes rugged and trembling"),
2777                                          "tools.calligraphic", "tremor", 0.0,
2778                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2779                                          0.0, 1.0, 0.01, 0.1,
2780                                          labels, values, G_N_ELEMENTS(labels),
2781                                          sp_ddc_tremor_value_changed, 0.01, 2 );
2783         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2784         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2785         }
2787         {
2788         /* Wiggle */
2789         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
2790         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
2791         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
2792                                          _("Wiggle:"), _("Increase to make the pen waver and wiggle"),
2793                                          "tools.calligraphic", "wiggle", 0.0,
2794                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2795                                          0.0, 1.0, 0.01, 0.1,
2796                                          labels, values, G_N_ELEMENTS(labels),
2797                                          sp_ddc_wiggle_value_changed, 0.01, 2 );
2798         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2799         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2800         }
2802         {
2803         /* Mass */
2804             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
2805         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
2806         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
2807                                          _("Mass:"), _("Increase to make the pen drag behind, as if slowed by inertia"),
2808                                          "tools.calligraphic", "mass", 0.02,
2809                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2810                                          0.0, 1.0, 0.01, 0.1,
2811                                          labels, values, G_N_ELEMENTS(labels),
2812                                          sp_ddc_mass_value_changed, 0.01, 2 );
2813         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2814         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2815         }
2818         /* Trace Background button */
2819         {
2820             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
2821                                                           _("Trace Background"),
2822                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
2823                                                           "trace_background",
2824                                                           Inkscape::ICON_SIZE_DECORATION );
2825             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2826             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
2827             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
2828         }
2830         /* Use Pressure button */
2831         {
2832             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
2833                                                           _("Pressure"),
2834                                                           _("Use the pressure of the input device to alter the width of the pen"),
2835                                                           "use_pressure",
2836                                                           Inkscape::ICON_SIZE_DECORATION );
2837             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2838             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
2839             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
2840         }
2842         /* Use Tilt button */
2843         {
2844             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
2845                                                           _("Tilt"),
2846                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
2847                                                           "use_tilt",
2848                                                           Inkscape::ICON_SIZE_DECORATION );
2849             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2850             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
2851             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2852             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2853         }
2855         /* Reset */
2856         {
2857             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
2858                                              _("Defaults"),
2859                                              _("Reset all parameters to defaults"),
2860                                              GTK_STOCK_CLEAR );
2861             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
2862             gtk_action_group_add_action( mainActions, act );
2863             gtk_action_set_sensitive( act, TRUE );
2864         }
2865     }
2869 //########################
2870 //##    Circle / Arc    ##
2871 //########################
2873 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
2875     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
2876     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
2878     if (v1 == 0 && v2 == 0) {
2879         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
2880             gtk_action_set_sensitive( ocb, FALSE );
2881             gtk_action_set_sensitive( make_whole, FALSE );
2882         }
2883     } else {
2884         gtk_action_set_sensitive( ocb, TRUE );
2885         gtk_action_set_sensitive( make_whole, TRUE );
2886     }
2889 static void
2890 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
2892     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2894     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2895         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
2896     }
2898     // quit if run by the attr_changed listener
2899     if (g_object_get_data( tbl, "freeze" )) {
2900         return;
2901     }
2903     // in turn, prevent listener from responding
2904     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2906     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2908     bool modmade = false;
2909     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2910          items != NULL;
2911          items = items->next)
2912     {
2913         SPItem *item = SP_ITEM(items->data);
2915         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
2917             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
2918             SPArc *arc = SP_ARC(item);
2920             if (!strcmp(value_name, "start"))
2921                 ge->start = (adj->value * M_PI)/ 180;
2922             else
2923                 ge->end = (adj->value * M_PI)/ 180;
2925             sp_genericellipse_normalize(ge);
2926             ((SPObject *)arc)->updateRepr();
2927             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
2929             modmade = true;
2930         }
2931     }
2933     g_free(namespaced_name);
2935     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
2937     sp_arctb_sensitivize( tbl, adj->value, other->value );
2939     if (modmade) {
2940         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
2941                                    _("Arc: Change start/end"));
2942     }
2944     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2948 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
2950     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
2953 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
2955     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
2958 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
2960     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2961     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2962         if ( ege_select_one_action_get_active( act ) != 0 ) {
2963             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
2964         } else {
2965             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
2966         }
2967     }
2969     // quit if run by the attr_changed listener
2970     if (g_object_get_data( tbl, "freeze" )) {
2971         return;
2972     }
2974     // in turn, prevent listener from responding
2975     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2977     bool modmade = false;
2979     if ( ege_select_one_action_get_active(act) != 0 ) {
2980         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2981              items != NULL;
2982              items = items->next)
2983         {
2984             if (SP_IS_ARC((SPItem *) items->data)) {
2985                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2986                 repr->setAttribute("sodipodi:open", "true");
2987                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2988                 modmade = true;
2989             }
2990         }
2991     } else {
2992         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2993              items != NULL;
2994              items = items->next)
2995         {
2996             if (SP_IS_ARC((SPItem *) items->data))    {
2997                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2998                 repr->setAttribute("sodipodi:open", NULL);
2999                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3000                 modmade = true;
3001             }
3002         }
3003     }
3005     if (modmade) {
3006         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3007                                    _("Arc: Change open/closed"));
3008     }
3010     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3013 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3015     GtkAdjustment *adj;
3016     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3017     gtk_adjustment_set_value(adj, 0.0);
3018     gtk_adjustment_value_changed(adj);
3020     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3021     gtk_adjustment_set_value(adj, 0.0);
3022     gtk_adjustment_value_changed(adj);
3024     spinbutton_defocus( GTK_OBJECT(obj) );
3027 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
3028                                       gchar const *old_value, gchar const *new_value,
3029                                       bool is_interactive, gpointer data)
3031     GObject *tbl = G_OBJECT(data);
3033     // quit if run by the _changed callbacks
3034     if (g_object_get_data( tbl, "freeze" )) {
3035         return;
3036     }
3038     // in turn, prevent callbacks from responding
3039     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3041     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3042     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3044     GtkAdjustment *adj1,*adj2;
3045     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3046     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3047     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3048     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3050     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3052     char const *openstr = NULL;
3053     openstr = repr->attribute("sodipodi:open");
3054     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3056     if (openstr) {
3057         ege_select_one_action_set_active( ocb, 1 );
3058     } else {
3059         ege_select_one_action_set_active( ocb, 0 );
3060     }
3062     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3065 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3066     NULL, /* child_added */
3067     NULL, /* child_removed */
3068     arc_tb_event_attr_changed,
3069     NULL, /* content_changed */
3070     NULL  /* order_changed */
3071 };
3074 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3076     int n_selected = 0;
3077     Inkscape::XML::Node *repr = NULL;
3079     purge_repr_listener( tbl, tbl );
3081     for (GSList const *items = selection->itemList();
3082          items != NULL;
3083          items = items->next)
3084     {
3085         if (SP_IS_ARC((SPItem *) items->data)) {
3086             n_selected++;
3087             repr = SP_OBJECT_REPR((SPItem *) items->data);
3088         }
3089     }
3091     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3093     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3094     if (n_selected == 0) {
3095         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3096     } else if (n_selected == 1) {
3097         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3098         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3100         if (repr) {
3101             g_object_set_data( tbl, "repr", repr );
3102             Inkscape::GC::anchor(repr);
3103             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3104             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3105         }
3106     } else {
3107         // FIXME: implement averaging of all parameters for multiple selected
3108         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3109         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3110         sp_arctb_sensitivize( tbl, 1, 0 );
3111     }
3115 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3117     EgeAdjustmentAction* eact = 0;
3120     {
3121         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3122         ege_output_action_set_use_markup( act, TRUE );
3123         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3124         g_object_set_data( holder, "mode_action", act );
3125     }
3127     /* Start */
3128     {
3129         eact = create_adjustment_action( "ArcStartAction",
3130                                          _("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
3131                                          "tools.shapes.arc", "start", 0.0,
3132                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3133                                          -360.0, 360.0, 1.0, 10.0,
3134                                          0, 0, 0,
3135                                          sp_arctb_start_value_changed);
3136         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3137     }
3139     /* End */
3140     {
3141         eact = create_adjustment_action( "ArcEndAction",
3142                                          _("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
3143                                          "tools.shapes.arc", "end", 0.0,
3144                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3145                                          -360.0, 360.0, 1.0, 10.0,
3146                                          0, 0, 0,
3147                                          sp_arctb_end_value_changed);
3148         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3149     }
3151     /* Segments / Pie checkbox */
3152     {
3153         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3155         GtkTreeIter iter;
3156         gtk_list_store_append( model, &iter );
3157         gtk_list_store_set( model, &iter,
3158                             0, _("Closed arc"),
3159                             1, _("Switch to segment (closed shape with two radii)"),
3160                             2, "circle_closed_arc",
3161                             -1 );
3163         gtk_list_store_append( model, &iter );
3164         gtk_list_store_set( model, &iter,
3165                             0, _("Open Arc"),
3166                             1, _("Switch to arc (unclosed shape)"),
3167                             2, "circle_open_arc",
3168                             -1 );
3170         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
3171         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3172         g_object_set_data( holder, "open_action", act );
3174         ege_select_one_action_set_appearance( act, "full" );
3175         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3176         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3177         ege_select_one_action_set_icon_column( act, 2 );
3178         ege_select_one_action_set_tooltip_column( act, 1  );
3180         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3181         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3182         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3183         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3184     }
3186     /* Make Whole */
3187     {
3188         InkAction* inky = ink_action_new( "ArcResetAction",
3189                                           _("Make whole"),
3190                                           _("Make the shape a whole ellipse, not arc or segment"),
3191                                           "reset_circle",
3192                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3193         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3194         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3195         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3196         g_object_set_data( holder, "make_whole", inky );
3197     }
3199     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3200     // sensitivize make whole and open checkbox
3201     {
3202         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3203         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3204         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3205     }
3208     sigc::connection *connection = new sigc::connection(
3209         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3210         );
3211     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3212     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3218 // toggle button callbacks and updaters
3220 //########################
3221 //##      Dropper       ##
3222 //########################
3224 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3225     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3226     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3227     if ( set_action ) {
3228         if ( gtk_toggle_action_get_active( act ) ) {
3229             gtk_action_set_sensitive( set_action, TRUE );
3230         } else {
3231             gtk_action_set_sensitive( set_action, FALSE );
3232         }
3233     }
3235     spinbutton_defocus(GTK_OBJECT(tbl));
3238 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3239     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3240     spinbutton_defocus(GTK_OBJECT(tbl));
3244 /**
3245  * Dropper auxiliary toolbar construction and setup.
3246  *
3247  * TODO: Would like to add swatch of current color.
3248  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3249  *       can drag and drop places. Will provide a nice mixing palette.
3250  */
3251 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3253     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3255     {
3256         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3257                                                       _("Pick alpha"),
3258                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3259                                                       "color_alpha_get",
3260                                                       Inkscape::ICON_SIZE_DECORATION );
3261         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3262         g_object_set_data( holder, "pick_action", act );
3263         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3264         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3265     }
3267     {
3268         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3269                                                       _("Set alpha"),
3270                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3271                                                       "color_alpha_set",
3272                                                       Inkscape::ICON_SIZE_DECORATION );
3273         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3274         g_object_set_data( holder, "set_action", act );
3275         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3276         // make sure it's disabled if we're not picking alpha
3277         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3278         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3279     }
3283 //########################
3284 //##    Text Toolbox    ##
3285 //########################
3286 /*
3287 static void
3288 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3290     //Call back for letter sizing spinbutton
3293 static void
3294 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3296     //Call back for line height spinbutton
3299 static void
3300 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3302     //Call back for horizontal kerning spinbutton
3305 static void
3306 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3308     //Call back for vertical kerning spinbutton
3311 static void
3312 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3314     //Call back for letter rotation spinbutton
3315 }*/
3317 namespace {
3319 bool visible = false;
3321 void
3322 sp_text_toolbox_selection_changed (Inkscape::Selection *selection, GObject *tbl)
3324     SPStyle *query =
3325         sp_style_new (SP_ACTIVE_DOCUMENT);
3327     int result_family =
3328         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3330     int result_style =
3331         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3333     int result_numbers =
3334         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3336     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3338     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3339     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3340     {
3341         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3343         if (repr)
3344         {
3345             sp_style_read_from_repr (query, repr);
3346         }
3347         else
3348         {
3349             return;
3350         }
3351     }
3353     if (query->text)
3354     {
3355         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3356             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3357             gtk_entry_set_text (GTK_ENTRY (entry), "");
3359         } else if (query->text->font_family.value) {
3361             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3362             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3364             Gtk::TreePath path;
3365             try {
3366                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3367             } catch (...) {
3368                 return;
3369             }
3371             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3372             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3374             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3376             gtk_tree_selection_select_path (tselection, path.gobj());
3377             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3379             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3380         }
3382         //Size
3383         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3384         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3385         g_object_set_data (tbl, "size-block", gpointer(1));
3386         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3387         g_object_set_data (tbl, "size-block", gpointer(0));
3388         free (str);
3390         //Anchor
3391         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3392         {
3393             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3394             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3395             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3396             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3397         }
3398         else
3399         {
3400             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3401             {
3402                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3403                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3404                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3405                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3406             }
3407             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3408             {
3409                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3410                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3411                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3412                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3413             }
3414             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3415             {
3416                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3417                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3418                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3419                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3420             }
3421         }
3423         //Style
3424         {
3425             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3427             gboolean active = gtk_toggle_button_get_active (button);
3428             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3430             if (active != check)
3431             {
3432                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3433                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3434                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3435             }
3436         }
3438         {
3439             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3441             gboolean active = gtk_toggle_button_get_active (button);
3442             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3444             if (active != check)
3445             {
3446                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3447                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3448                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3449             }
3450         }
3452         //Orientation
3453         //locking both buttons, changing one affect all group (both)
3454         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3455         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3457         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3458         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3460         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3461         {
3462             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3463         }
3464         else
3465         {
3466             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3467         }
3468         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3469         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3470     }
3472     sp_style_unref(query);
3475 void
3476 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint flags, GObject *tbl)
3478     sp_text_toolbox_selection_changed (selection, tbl);
3481 void
3482 sp_text_toolbox_subselection_changed (gpointer dragger, GObject *tbl)
3484     sp_text_toolbox_selection_changed (NULL, tbl);
3487 void
3488 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3489                                 GObject             *tbl)
3491     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3492     GtkTreeModel *model = 0;
3493     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3494     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3495     GtkTreeIter   iter;
3496     char         *family = 0;
3498     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3499     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3501     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3502         return;
3503     }
3505     gtk_tree_model_get (model, &iter, 0, &family, -1);
3507     if (g_object_get_data (G_OBJECT (selection), "block"))
3508     {
3509         gtk_entry_set_text (GTK_ENTRY (entry), family);
3510         return;
3511     }
3513     gtk_widget_hide (popdown);
3514     visible = false;
3516     gtk_entry_set_text (GTK_ENTRY (entry), family);
3518     SPStyle *query =
3519         sp_style_new (SP_ACTIVE_DOCUMENT);
3521     int result_numbers =
3522         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3524     SPCSSAttr *css = sp_repr_css_attr_new ();
3525     sp_repr_css_set_property (css, "font-family", family);
3527     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3528     if (result_numbers == QUERY_STYLE_NOTHING)
3529     {
3530         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3531         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3532     }
3533     else
3534     {
3535         sp_desktop_set_style (desktop, css, true, true);
3536     }
3538     sp_style_unref(query);
3540     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3541                                    _("Text: Change font family"));
3542     sp_repr_css_attr_unref (css);
3543     free (family);
3544     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3546     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3549 void
3550 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3551                                        GObject      *tbl)
3553     const char *family = gtk_entry_get_text (entry);
3555     try {
3556         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3557         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3558         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3559         gtk_tree_selection_select_path (selection, path.gobj());
3560         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3561         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3562     } catch (...) {
3563         if (family && strlen (family))
3564         {
3565             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3566         }
3567     }
3570 void
3571 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3572                                    gpointer          data)
3574     if (g_object_get_data (G_OBJECT (button), "block")) return;
3575     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3576     int prop = GPOINTER_TO_INT(data);
3578     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3579     SPCSSAttr *css = sp_repr_css_attr_new ();
3581     switch (prop)
3582     {
3583         case 0:
3584         {
3585             sp_repr_css_set_property (css, "text-anchor", "start");
3586             sp_repr_css_set_property (css, "text-align", "start");
3587             break;
3588         }
3589         case 1:
3590         {
3591             sp_repr_css_set_property (css, "text-anchor", "middle");
3592             sp_repr_css_set_property (css, "text-align", "center");
3593             break;
3594         }
3596         case 2:
3597         {
3598             sp_repr_css_set_property (css, "text-anchor", "end");
3599             sp_repr_css_set_property (css, "text-align", "end");
3600             break;
3601         }
3603         case 3:
3604         {
3605             sp_repr_css_set_property (css, "text-anchor", "start");
3606             sp_repr_css_set_property (css, "text-align", "justify");
3607             break;
3608         }
3609     }
3611     SPStyle *query =
3612         sp_style_new (SP_ACTIVE_DOCUMENT);
3613     int result_numbers =
3614         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3616     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3617     if (result_numbers == QUERY_STYLE_NOTHING)
3618     {
3619         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3620     }
3622     sp_style_unref(query);
3624     sp_desktop_set_style (desktop, css, true, true);
3625     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3626                                    _("Text: Change alignment"));
3627     sp_repr_css_attr_unref (css);
3629     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3632 void
3633 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
3634                                gpointer          data)
3636     if (g_object_get_data (G_OBJECT (button), "block")) return;
3638     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3639     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3640     int          prop       = GPOINTER_TO_INT(data);
3641     bool         active     = gtk_toggle_button_get_active (button);
3644     switch (prop)
3645     {
3646         case 0:
3647         {
3648             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
3649             break;
3650         }
3652         case 1:
3653         {
3654             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
3655             break;
3656         }
3657     }
3659     SPStyle *query =
3660         sp_style_new (SP_ACTIVE_DOCUMENT);
3661     int result_numbers =
3662         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3664     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3665     if (result_numbers == QUERY_STYLE_NOTHING)
3666     {
3667         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3668     }
3670     sp_style_unref(query);
3672     sp_desktop_set_style (desktop, css, true, true);
3673     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3674                                    _("Text: Change font style"));
3675     sp_repr_css_attr_unref (css);
3677     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3680 void
3681 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
3682                                      gpointer         data)
3684     if (g_object_get_data (G_OBJECT (button), "block")) {
3685         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3686         return;
3687     }
3689     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3690     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3691     int          prop       = GPOINTER_TO_INT(data);
3693     switch (prop)
3694     {
3695         case 0:
3696         {
3697             sp_repr_css_set_property (css, "writing-mode", "lr");
3698             break;
3699         }
3701         case 1:
3702         {
3703             sp_repr_css_set_property (css, "writing-mode", "tb");
3704             break;
3705         }
3706     }
3708     SPStyle *query =
3709         sp_style_new (SP_ACTIVE_DOCUMENT);
3710     int result_numbers =
3711         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3713     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3714     if (result_numbers == QUERY_STYLE_NOTHING)
3715     {
3716         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3717     }
3719     sp_desktop_set_style (desktop, css, true, true);
3720     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3721                                    _("Text: Change orientation"));
3722     sp_repr_css_attr_unref (css);
3724     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3727 gboolean
3728 sp_text_toolbox_size_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
3730     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3731     if (!desktop) return FALSE;
3733     switch (get_group0_keyval (event)) {
3734         case GDK_Escape: // defocus
3735             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3736             return TRUE; // I consumed the event
3737             break;
3738         case GDK_Return: // defocus
3739         case GDK_KP_Enter:
3740             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3741             return TRUE; // I consumed the event
3742             break;
3743     }
3744     return FALSE;
3747 gboolean
3748 sp_text_toolbox_family_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3750     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3751     if (!desktop) return FALSE;
3753     switch (get_group0_keyval (event)) {
3754         case GDK_Escape: // defocus
3755             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3756             sp_text_toolbox_selection_changed (NULL, tbl); // update
3757             return TRUE; // I consumed the event
3758             break;
3759     }
3760     return FALSE;
3763 gboolean
3764 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3766     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3767     if (!desktop) return FALSE;
3769     switch (get_group0_keyval (event)) {
3770         case GDK_Escape: // defocus
3771             gtk_widget_hide (w);
3772             visible = false;
3773             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3774             return TRUE; // I consumed the event
3775             break;
3776     }
3777     return FALSE;
3781 void
3782 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
3783                                GObject     *tbl)
3785     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3787     if (g_object_get_data (tbl, "size-block")) return;
3789 #if GTK_CHECK_VERSION(2,6,0)
3790     char *text = gtk_combo_box_get_active_text (cbox);
3791 #else // GTK_CHECK_VERSION(2,6,0)
3792     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
3793     GtkTreeIter iter;
3794     char *text = NULL;
3796     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
3797         gtk_tree_model_get (model, &iter, 0, &text, -1);
3798 #endif // GTK_CHECK_VERSION(2,6,0)
3800     SPCSSAttr *css = sp_repr_css_attr_new ();
3801     sp_repr_css_set_property (css, "font-size", text);
3802     free (text);
3804     SPStyle *query =
3805         sp_style_new (SP_ACTIVE_DOCUMENT);
3806     int result_numbers =
3807         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3809     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3810     if (result_numbers == QUERY_STYLE_NOTHING)
3811     {
3812         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3813     }
3815     sp_style_unref(query);
3817     sp_desktop_set_style (desktop, css, true, true);
3818     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
3819                                    _("Text: Change font size"));
3820     sp_repr_css_attr_unref (css);
3823     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
3824         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3827 void
3828 sp_text_toolbox_text_popdown_clicked    (GtkButton          *button,
3829                                          GObject            *tbl)
3831     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3832     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3833     int x, y;
3835     if (!visible)
3836     {
3837         gdk_window_get_origin (widget->window, &x, &y);
3838         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
3839         gtk_widget_show_all (popdown);
3841         gdk_pointer_grab (widget->window, TRUE,
3842                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
3843                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
3844                                         GDK_POINTER_MOTION_MASK),
3845                           NULL, NULL, GDK_CURRENT_TIME);
3847         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
3849         visible = true;
3850     }
3851     else
3852     {
3853         gdk_pointer_ungrab (GDK_CURRENT_TIME);
3854         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3855         gtk_widget_hide (popdown);
3856         visible = false;
3857     }
3860 gboolean
3861 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
3862                                  GdkEventFocus    *event,
3863                                  GObject          *tbl)
3865     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
3866     return FALSE;
3869 gboolean
3870 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
3871                                    GdkEventFocus    *event,
3872                                    GObject          *tbl)
3874     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3876     gtk_widget_hide (popdown);
3877     visible = false;
3878     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3879     return TRUE;
3882 void
3883 cell_data_func  (GtkTreeViewColumn *column,
3884                  GtkCellRenderer   *cell,
3885                  GtkTreeModel      *tree_model,
3886                  GtkTreeIter       *iter,
3887                  gpointer           data)
3889     char        *family,
3890         *family_escaped,
3891         *sample_escaped;
3893     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
3895     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
3897     family_escaped = g_markup_escape_text (family, -1);
3898     sample_escaped = g_markup_escape_text (sample, -1);
3900     std::stringstream markup;
3901     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
3902     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
3904     free (family);
3905     free (family_escaped);
3906     free (sample_escaped);
3909 static void delete_completion(GObject *obj, GtkWidget *entry) {
3910     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
3911     if (completion) {
3912         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
3913         g_object_unref (completion);
3914     }
3917 GtkWidget*
3918 sp_text_toolbox_new (SPDesktop *desktop)
3920     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
3922     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
3923     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
3925     GtkTooltips *tt = gtk_tooltips_new();
3926     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
3928     ////////////Family
3929     //Window
3930     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3931     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
3933     //Entry
3934     GtkWidget           *entry = gtk_entry_new ();
3935     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
3936     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
3937     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
3938     gtk_entry_completion_set_text_column (completion, 0);
3939     gtk_entry_completion_set_minimum_key_length (completion, 1);
3940     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
3941     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
3942     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
3943     aux_toolbox_space (tbl, 1);
3944     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
3945     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
3947     //Button
3948     GtkWidget   *button = gtk_button_new ();
3949     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
3950     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
3952     //Popdown
3953     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
3954     GtkWidget           *treeview = gtk_tree_view_new ();
3956     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
3957     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
3958     gtk_tree_view_column_pack_start (column, cell, FALSE);
3959     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
3960     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
3961     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
3963     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
3964     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
3965 #if GTK_CHECK_VERSION(2,6,0)
3966     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
3967 #endif // GTK_CHECK_VERSION(2,6,0)
3969     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
3971     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
3972     gtk_container_add (GTK_CONTAINER (sw), treeview);
3974     gtk_container_add (GTK_CONTAINER (window), sw);
3975     gtk_widget_set_size_request (window, 300, 450);
3977     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
3978     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
3979     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
3981     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
3983     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
3984     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
3986     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
3987     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
3989     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
3990     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
3991     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
3992     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
3993     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
3995     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
3996     aux_toolbox_space (tbl, 1);
3997     GtkWidget *box = gtk_event_box_new ();
3998     gtk_container_add (GTK_CONTAINER (box), image);
3999     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4000     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4001     GtkTooltips *tooltips = gtk_tooltips_new ();
4002     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4003     gtk_widget_hide (GTK_WIDGET (box));
4004     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4006     ////////////Size
4007     const char *sizes[] = {
4008         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4009         "16", "18", "20", "22", "24", "28",
4010         "32", "36", "40", "48", "56", "64", "72", "144"
4011     };
4013     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4014     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4015     gtk_widget_set_size_request (cbox, 80, -1);
4016     aux_toolbox_space (tbl, 1);
4017     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4018     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4019     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4020     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4022     //spacer
4023     aux_toolbox_space (tbl, 4);
4024     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4026     ////////////Text anchor
4027     GtkWidget *group   = gtk_radio_button_new (NULL);
4028     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4029     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4031     // left
4032     GtkWidget *rbutton = group;
4033     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4034     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4035     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4037     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4038     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4039     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4040     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4042     // center
4043     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4044     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4045     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4046     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4048     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4049     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4050     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4051     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4053     // right
4054     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4055     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4056     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4057     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4059     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4060     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4061     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4062     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4064     // fill
4065     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4066     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4067     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4068     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4070     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4071     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4072     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4073     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4075     aux_toolbox_space (tbl, 1);
4076     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4078     //spacer
4079     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4081     ////////////Text style
4082     row = gtk_hbox_new (FALSE, 4);
4084     // bold
4085     rbutton = gtk_toggle_button_new ();
4086     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4087     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4088     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4089     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4091     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4092     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4093     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4095     // italic
4096     rbutton = gtk_toggle_button_new ();
4097     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4098     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4099     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4100     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4102     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4103     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4104     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4106     aux_toolbox_space (tbl, 1);
4107     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4109     //spacer
4110     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4112     ////////////Text orientation
4113     group   = gtk_radio_button_new (NULL);
4114     row     = gtk_hbox_new (FALSE, 4);
4115     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4117     // horizontal
4118     rbutton = group;
4119     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4120     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4121     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4122     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4124     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4125     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4126     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4128     // vertical
4129     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4130     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4131     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4132     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4133     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4135     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4136     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4137     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4138     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4141     //watch selection
4142     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4144     sigc::connection *c_selection_changed =
4145         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4146                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4147     pool->add_connection ("selection-changed", c_selection_changed);
4149     sigc::connection *c_selection_modified =
4150         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4151                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4152     pool->add_connection ("selection-modified", c_selection_modified);
4154     sigc::connection *c_subselection_changed =
4155         new sigc::connection (desktop->connectToolSubselectionChanged
4156                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4157     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4159     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4162 #if 0
4163     // horizontal
4164     {
4165         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4166         GtkWidget *b = group = gtk_radio_button_new (NULL);
4167         gtk_container_add (GTK_CONTAINER (b), px);
4168         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4169         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4170         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4171         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4172     }
4174     // vertical
4175     {
4176         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4177         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4178         gtk_container_add (GTK_CONTAINER (b), px);
4179         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4180         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4181         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4182         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4183     }
4185     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4187     // letter spacing
4188     {
4189         {
4190             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4191             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4192             gtk_container_add (GTK_CONTAINER (hb), image);
4193             gtk_widget_show(image);
4194             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4195         }
4197         {
4198             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between letters"),
4199                                              "tools.text", "letter_spacing", 0.0,
4200                                              us, tbl, FALSE, NULL,
4201                                              -1000.0, 1000.0, 0.1, 0.1,
4202                                              sp_text_letter_changed, 0.1, 1);
4203             gtk_widget_set_size_request (hb, 45, 6);
4204             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4205         }
4206     }
4208     // line spacing
4209     {
4210         {
4211             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4212             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4213             gtk_container_add (GTK_CONTAINER (hb), image);
4214             gtk_widget_show(image);
4215             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4216         }
4218         {
4219             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between lines"),
4220                                              "tools.text", "line_spacing", 0,
4221                                              us, tbl, FALSE, NULL,
4222                                              -1000.0, 1000.0, 0.1, 0.1,
4223                                              sp_text_line_changed, 0.1, 1);
4224             gtk_widget_set_size_request (hb, 45, 0);
4225             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4226         }
4227     }
4229     {
4230         // horizontal kerning/vertical kerning units menu: create
4231         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4232         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4233         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4235         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4237         // horizontal kerning
4238         {
4239             {
4240                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4241                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4242                 gtk_container_add (GTK_CONTAINER (hb), image);
4243                 gtk_widget_show(image);
4244                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4245             }
4247             {
4248                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Horizontal kerning"),
4249                                                  "tools.text", "horizontal_kerning", 0,
4250                                                  us, tbl, FALSE, NULL,
4251                                                  -100.00, 100.00, 0.01, 0.1,
4252                                                  sp_text_horiz_kern_changed);
4253                 gtk_widget_set_size_request (hb, 45, 0);
4254                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4255             }
4256         }
4258         // vertical kerning
4259         {
4260             {
4261                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4262                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4263                 gtk_container_add (GTK_CONTAINER (hb), image);
4264                 gtk_widget_show(image);
4265                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4266             }
4268             {
4269                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Vertical kerning"),
4270                                                  "tools.text", "vertical_kerning", 0,
4271                                                  us, tbl, FALSE, NULL,
4272                                                  -100.00, 100.00, 0.01, 0.1,
4273                                                  sp_text_vert_kern_changed);
4274                 gtk_widget_set_size_request (hb, 45, 0);
4275                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4276             }
4277         }
4279         // add the units menu
4280         gtk_widget_show(us);
4281         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4282         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4283     }
4285     // letter rotation
4286     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4287     {
4288         {
4289             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4290             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4291             gtk_container_add (GTK_CONTAINER (hb), image);
4292             gtk_widget_show(image);
4293             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4294         }
4295         {
4296             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Letter rotation"),
4297                                              "tools.text", "letter_rotation", 0,
4298                                              us, tbl, FALSE, NULL,
4299                                              -180.0, 180.0, 0.1, 0.1,
4300                                              sp_text_letter_rotation_changed, 0.1, 1);
4301             gtk_widget_set_size_request (hb, 45, 0);
4302             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4303         }
4304         // rotation degree label
4305         {
4306             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4307             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4308         }
4309     }
4311     // Remove Manual Kerns
4312     {
4313         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4314         GtkWidget *button = gtk_button_new ();
4315         gtk_container_add (GTK_CONTAINER (button), px);
4316         gtk_widget_show(button);
4317         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4318         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4319         gtk_widget_set_sensitive(button, TRUE);
4320         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4321     }
4322 #endif
4324     gtk_widget_show_all (tbl);
4325     return tbl;
4327 } // end of sp_text_toolbox_new()
4329 }//<unnamed> namespace
4332 //#########################
4333 //##  Connector Toolbox  ##
4334 //#########################
4336 static void sp_connector_path_set_avoid(void)
4338     cc_selection_set_avoid(true);
4342 static void sp_connector_path_set_ignore(void)
4344     cc_selection_set_avoid(false);
4349 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4351     // quit if run by the _changed callbacks
4352     if (g_object_get_data( tbl, "freeze" )) {
4353         return;
4354     }
4356     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4357     SPDocument *doc = sp_desktop_document(desktop);
4359     if (!sp_document_get_undo_sensitive(doc))
4360     {
4361         return;
4362     }
4364     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4366     if ( repr->attribute("inkscape:connector-spacing") ) {
4367         gdouble priorValue = gtk_adjustment_get_value(adj);
4368         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4369         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4370             return;
4371         }
4372     } else if ( adj->value == defaultConnSpacing ) {
4373         return;
4374     }
4376     // in turn, prevent callbacks from responding
4377     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4379     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4380     SP_OBJECT(desktop->namedview)->updateRepr();
4382     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4383     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4384         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4385         NR::Matrix m = NR::identity();
4386         avoid_item_move(&m, item);
4387     }
4389     if (items) {
4390         g_slist_free(items);
4391     }
4393     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4394             _("Change connector spacing"));
4396     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4398     spinbutton_defocus(GTK_OBJECT(tbl));
4401 static void sp_connector_graph_layout(void)
4403     if (!SP_ACTIVE_DESKTOP) return;
4405     // hack for clones, see comment in align-and-distribute.cpp
4406     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4407     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4409     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4411     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4413     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4416 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4418     if ( gtk_toggle_action_get_active( act ) ) {
4419         prefs_set_string_attribute("tools.connector", "directedlayout",
4420                 "true");
4421     } else {
4422         prefs_set_string_attribute("tools.connector", "directedlayout",
4423                 "false");
4424     }
4427 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4429     if ( gtk_toggle_action_get_active( act ) ) {
4430         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4431                 "true");
4432     } else {
4433         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4434                 "false");
4435     }
4439 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4441     prefs_set_double_attribute("tools.connector", "length", adj->value);
4442     spinbutton_defocus(GTK_OBJECT(tbl));
4445 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4446         gchar const *name, gchar const *old_value, gchar const *new_value,
4447         bool is_interactive, gpointer data)
4449     GtkWidget *tbl = GTK_WIDGET(data);
4451     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4452         return;
4453     }
4454     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4455         return;
4456     }
4458     GtkAdjustment *adj = (GtkAdjustment*)
4459             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4460     gdouble spacing = defaultConnSpacing;
4461     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4463     gtk_adjustment_set_value(adj, spacing);
4467 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4468     NULL, /* child_added */
4469     NULL, /* child_removed */
4470     connector_tb_event_attr_changed,
4471     NULL, /* content_changed */
4472     NULL  /* order_changed */
4473 };
4476 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4478     {
4479         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4480                                           _("Avoid"),
4481                                           _("Make connectors avoid selected objects"),
4482                                           "connector_avoid",
4483                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4484         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
4485         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4486     }
4488     {
4489         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
4490                                           _("Ignore"),
4491                                           _("Make connectors ignore selected objects"),
4492                                           "connector_ignore",
4493                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4494         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
4495         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4496     }
4498     EgeAdjustmentAction* eact = 0;
4500     // Spacing spinbox
4501     eact = create_adjustment_action( "ConnectorSpacingAction",
4502                                      _("Spacing:"), _("The amount of space left around objects by auto-routing connectors"),
4503                                      "tools.connector", "spacing", defaultConnSpacing,
4504                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
4505                                      0, 100, 1.0, 10.0,
4506                                      0, 0, 0,
4507                                      connector_spacing_changed, 1, 0 );
4508     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4510     // Graph (connector network) layout
4511     {
4512         InkAction* inky = ink_action_new( "ConnectorGraphAction",
4513                                           _("Graph"),
4514                                           _("Nicely arrange selected connector network"),
4515                                           "graph_layout",
4516                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4517         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
4518         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4519     }
4521     // Default connector length spinbox
4522     eact = create_adjustment_action( "ConnectorLengthAction",
4523                                      _("Length:"), _("Ideal length for connectors when layout is applied"),
4524                                      "tools.connector", "length", 100,
4525                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
4526                                      10, 1000, 10.0, 100.0,
4527                                      0, 0, 0,
4528                                      connector_length_changed, 1, 0 );
4529     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4532     // Directed edges toggle button
4533     {
4534         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
4535                                                       _("Downwards"),
4536                                                       _("Make connectors with end-markers (arrows) point downwards"),
4537                                                       "directed_graph",
4538                                                       Inkscape::ICON_SIZE_DECORATION );
4539         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4541         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
4542         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4543                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4545         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
4546     }
4548     // Avoid overlaps toggle button
4549     {
4550         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
4551                                                       _("Remove overlaps"),
4552                                                       _("Do not allow overlapping shapes"),
4553                                                       "remove_overlaps",
4554                                                       Inkscape::ICON_SIZE_DECORATION );
4555         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4557         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
4558         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4559                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4561         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
4562     }
4564     // Code to watch for changes to the connector-spacing attribute in
4565     // the XML.
4566     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4567     g_assert(repr != NULL);
4569     purge_repr_listener( holder, holder );
4571     if (repr) {
4572         g_object_set_data( holder, "repr", repr );
4573         Inkscape::GC::anchor(repr);
4574         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
4575         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
4576     }
4577 } // end of sp_connector_toolbox_prep()
4579 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* tbl)
4581     gint channels = ege_select_one_action_get_active( act );
4582     flood_channels_set_channels( channels );
4585 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject *tbl)
4587     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4590 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject *tbl)
4592     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
4595 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
4597     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
4598     SPUnit const *unit = tracker->getActiveUnit();
4601     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4604 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
4606     // FIXME: make defaults settable via Inkscape Options
4607     struct KeyValue {
4608         char const *key;
4609         double value;
4610     } const key_values[] = {
4611         {"threshold", 15},
4612         {"offset", 0.0}
4613     };
4615     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
4616         KeyValue const &kv = key_values[i];
4617         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
4618         if ( adj ) {
4619             gtk_adjustment_set_value(adj, kv.value);
4620         }
4621     }
4622     
4623     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
4624     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
4625     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
4626     ege_select_one_action_set_active( autogap_action, 0 );
4629 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4631     EgeAdjustmentAction* eact = 0;
4633     {
4634         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4636         GList* items = 0;
4637         gint count = 0;
4638         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
4639         {
4640             GtkTreeIter iter;
4641             gtk_list_store_append( model, &iter );
4642             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4643             count++;
4644         }
4645         g_list_free( items );
4646         items = 0;
4647         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by:"), _(""), NULL, GTK_TREE_MODEL(model) );
4648         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
4649         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
4650         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4651         g_object_set_data( holder, "channels_action", act1 );
4652     }
4654     // Spacing spinbox
4655     {
4656         eact = create_adjustment_action(
4657             "ThresholdAction",
4658             _("Threshold:"),
4659             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
4660             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
4661             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
4662             0, 0, 0,
4663             paintbucket_threshold_changed, 1, 0 );
4665         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4666     }
4668     // Create the units menu.
4669     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
4670     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
4671     g_object_set_data( holder, "tracker", tracker );
4672     {
4673         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), _("") );
4674         gtk_action_group_add_action( mainActions, act );
4675     }
4677     // Offset spinbox
4678     {
4679         eact = create_adjustment_action(
4680             "OffsetAction",
4681             _("Grow/shrink by:"),
4682             _("The amount to grow (positive) or shrink (negative) the created fill path"),
4683             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
4684             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
4685             0, 0, 0,
4686             paintbucket_offset_changed, 1, 2);
4687         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4689         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4690     }
4691     
4692     /* Auto Gap */
4693     {
4694         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4696         GList* items = 0;
4697         gint count = 0;
4698         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
4699         {
4700             GtkTreeIter iter;
4701             gtk_list_store_append( model, &iter );
4702             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4703             count++;
4704         }
4705         g_list_free( items );
4706         items = 0;
4707         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Fill gaps:"), _(""), NULL, GTK_TREE_MODEL(model) );
4708         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
4709         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
4710         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
4711         g_object_set_data( holder, "autogap_action", act2 );
4712     }
4714     /* Reset */
4715     {
4716         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
4717                                           _("Defaults"),
4718                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4719                                           GTK_STOCK_CLEAR );
4720         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
4721         gtk_action_group_add_action( mainActions, act );
4722         gtk_action_set_sensitive( act, TRUE );
4723     }
4727 /*
4728   Local Variables:
4729   mode:c++
4730   c-file-style:"stroustrup"
4731   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4732   indent-tabs-mode:nil
4733   fill-column:99
4734   End:
4735 */
4736 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :