Code

make delete-preserving-shape the default used by the toolbar delete button
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "sp-clippath.h"
78 #include "sp-mask.h"
79 #include "style.h"
80 #include "selection.h"
81 #include "selection-chemistry.h"
82 #include "document-private.h"
83 #include "desktop-style.h"
84 #include "../libnrtype/font-lister.h"
85 #include "../libnrtype/font-instance.h"
86 #include "../connection-pool.h"
87 #include "../prefs-utils.h"
88 #include "../inkscape-stock.h"
89 #include "icon.h"
90 #include "graphlayout/graphlayout.h"
92 #include "mod360.h"
94 #include "toolbox.h"
96 #include "flood-context.h"
98 #include "ink-action.h"
99 #include "ege-adjustment-action.h"
100 #include "ege-output-action.h"
101 #include "ege-select-one-action.h"
102 #include "helper/unit-tracker.h"
104 #include "svg/css-ostringstream.h"
106 #include "widgets/calligraphic-profile-rename.h"
108 using Inkscape::UnitTracker;
110 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
111 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
113 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
126 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
132 static struct {
133     gchar const *type_name;
134     gchar const *data_name;
135     sp_verb_t verb;
136     sp_verb_t doubleclick_verb;
137 } const tools[] = {
138     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
139     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
140     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
141     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
142     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
143     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
144     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
145     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
146     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
147     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
148     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
149     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
150     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
151     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
152     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
153     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
154     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
155     { NULL, NULL, 0, 0 }
156 };
158 static struct {
159     gchar const *type_name;
160     gchar const *data_name;
161     GtkWidget *(*create_func)(SPDesktop *desktop);
162     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
163     gchar const *ui_name;
164     gint swatch_verb_id;
165     gchar const *swatch_tool;
166     gchar const *swatch_tip;
167 } const aux_toolboxes[] = {
168     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
169       SP_VERB_INVALID, 0, 0},
170     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
171       SP_VERB_INVALID, 0, 0},
172     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
173       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
174     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
175       SP_VERB_INVALID, 0, 0},
176     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
177       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
178     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
179       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
180     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
181       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
182     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
183       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
184     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
185       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
186     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
187       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
188     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
189       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
190     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
191       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
192     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
193       SP_VERB_INVALID, 0, 0},
194     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
195       SP_VERB_INVALID, 0, 0},
196     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
197       SP_VERB_INVALID, 0, 0},
198     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
199       SP_VERB_INVALID, 0, 0},
200     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
201       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
202     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
203 };
206 static gchar const * ui_descr =
207         "<ui>"
208         "  <toolbar name='SelectToolbar'>"
209         "    <toolitem action='EditSelectAll' />"
210         "    <toolitem action='EditSelectAllInAllLayers' />"
211         "    <toolitem action='EditDeselect' />"
212         "    <separator />"
213         "    <toolitem action='ObjectRotate90CCW' />"
214         "    <toolitem action='ObjectRotate90' />"
215         "    <toolitem action='ObjectFlipHorizontally' />"
216         "    <toolitem action='ObjectFlipVertically' />"
217         "    <separator />"
218         "    <toolitem action='SelectionToBack' />"
219         "    <toolitem action='SelectionLower' />"
220         "    <toolitem action='SelectionRaise' />"
221         "    <toolitem action='SelectionToFront' />"
222         "    <separator />"
223         "    <toolitem action='XAction' />"
224         "    <toolitem action='YAction' />"
225         "    <toolitem action='WidthAction' />"
226         "    <toolitem action='LockAction' />"
227         "    <toolitem action='HeightAction' />"
228         "    <toolitem action='UnitsAction' />"
229         "    <separator />"
230         "    <toolitem action='transform_affect_label' />"
231         "    <toolitem action='transform_stroke' />"
232         "    <toolitem action='transform_corners' />"
233         "    <toolitem action='transform_gradient' />"
234         "    <toolitem action='transform_pattern' />"
235         "  </toolbar>"
237         "  <toolbar name='NodeToolbar'>"
238         "    <toolitem action='NodeInsertAction' />"
239         "    <toolitem action='NodeDeleteAction' />"
240         "    <separator />"
241         "    <toolitem action='NodeJoinAction' />"
242         "    <toolitem action='NodeBreakAction' />"
243         "    <separator />"
244         "    <toolitem action='NodeJoinSegmentAction' />"
245         "    <toolitem action='NodeDeleteSegmentAction' />"
246         "    <separator />"
247         "    <toolitem action='NodeCuspAction' />"
248         "    <toolitem action='NodeSmoothAction' />"
249         "    <toolitem action='NodeSymmetricAction' />"
250         "    <separator />"
251         "    <toolitem action='NodeLineAction' />"
252         "    <toolitem action='NodeCurveAction' />"
253         "    <separator />"
254         "    <toolitem action='ObjectToPath' />"
255         "    <toolitem action='StrokeToPath' />"
256         "    <separator />"
257         "    <toolitem action='NodeXAction' />"
258         "    <toolitem action='NodeYAction' />"
259         "    <toolitem action='NodeUnitsAction' />"
260         "    <separator />"
261         "    <toolitem action='ObjectEditClipPathAction' />"
262         "    <toolitem action='ObjectEditMaskPathAction' />"
263         "    <toolitem action='EditNextLPEParameterAction' />"
264         "    <separator />"
265         "    <toolitem action='NodesShowHandlesAction' />"
266         "    <toolitem action='NodesShowHelperpath' />"
267         "  </toolbar>"
269         "  <toolbar name='TweakToolbar'>"
270         "    <toolitem action='TweakWidthAction' />"
271         "    <separator />"
272         "    <toolitem action='TweakForceAction' />"
273         "    <toolitem action='TweakPressureAction' />"
274         "    <separator />"
275         "    <toolitem action='TweakModeAction' />"
276         "    <separator />"
277         "    <toolitem action='TweakFidelityAction' />"
278         "    <separator />"
279         "    <toolitem action='TweakChannelsLabel' />"
280         "    <toolitem action='TweakDoH' />"
281         "    <toolitem action='TweakDoS' />"
282         "    <toolitem action='TweakDoL' />"
283         "    <toolitem action='TweakDoO' />"
284         "  </toolbar>"
286         "  <toolbar name='ZoomToolbar'>"
287         "    <toolitem action='ZoomIn' />"
288         "    <toolitem action='ZoomOut' />"
289         "    <separator />"
290         "    <toolitem action='Zoom1:0' />"
291         "    <toolitem action='Zoom1:2' />"
292         "    <toolitem action='Zoom2:1' />"
293         "    <separator />"
294         "    <toolitem action='ZoomSelection' />"
295         "    <toolitem action='ZoomDrawing' />"
296         "    <toolitem action='ZoomPage' />"
297         "    <toolitem action='ZoomPageWidth' />"
298         "    <separator />"
299         "    <toolitem action='ZoomPrev' />"
300         "    <toolitem action='ZoomNext' />"
301         "  </toolbar>"
303         "  <toolbar name='StarToolbar'>"
304         "    <separator />"
305         "    <toolitem action='StarStateAction' />"
306         "    <separator />"
307         "    <toolitem action='FlatAction' />"
308         "    <separator />"
309         "    <toolitem action='MagnitudeAction' />"
310         "    <toolitem action='SpokeAction' />"
311         "    <toolitem action='RoundednessAction' />"
312         "    <toolitem action='RandomizationAction' />"
313         "    <separator />"
314         "    <toolitem action='StarResetAction' />"
315         "  </toolbar>"
317         "  <toolbar name='RectToolbar'>"
318         "    <toolitem action='RectStateAction' />"
319         "    <toolitem action='RectWidthAction' />"
320         "    <toolitem action='RectHeightAction' />"
321         "    <toolitem action='RadiusXAction' />"
322         "    <toolitem action='RadiusYAction' />"
323         "    <toolitem action='RectUnitsAction' />"
324         "    <separator />"
325         "    <toolitem action='RectResetAction' />"
326         "  </toolbar>"
328         "  <toolbar name='3DBoxToolbar'>"
329         "    <toolitem action='3DBoxAngleXAction' />"
330         "    <toolitem action='3DBoxVPXStateAction' />"
331         "    <separator />"
332         "    <toolitem action='3DBoxAngleYAction' />"
333         "    <toolitem action='3DBoxVPYStateAction' />"
334         "    <separator />"
335         "    <toolitem action='3DBoxAngleZAction' />"
336         "    <toolitem action='3DBoxVPZStateAction' />"
337         "  </toolbar>"
339         "  <toolbar name='SpiralToolbar'>"
340         "    <toolitem action='SpiralStateAction' />"
341         "    <toolitem action='SpiralRevolutionAction' />"
342         "    <toolitem action='SpiralExpansionAction' />"
343         "    <toolitem action='SpiralT0Action' />"
344         "    <separator />"
345         "    <toolitem action='SpiralResetAction' />"
346         "  </toolbar>"
348         "  <toolbar name='PenToolbar'>"
349         "  </toolbar>"
351         "  <toolbar name='PencilToolbar'>"
352         "  </toolbar>"
354         "  <toolbar name='CalligraphyToolbar'>"
355         "    <separator />"
356         "    <toolitem action='SetProfileAction'/>"
357         "    <toolitem action='SaveDeleteProfileAction'/>"
358         "    <separator />"
359         "    <toolitem action='CalligraphyWidthAction' />"
360         "    <toolitem action='PressureAction' />"
361         "    <toolitem action='TraceAction' />"
362         "    <toolitem action='ThinningAction' />"
363         "    <separator />"
364         "    <toolitem action='AngleAction' />"
365         "    <toolitem action='TiltAction' />"
366         "    <toolitem action='FixationAction' />"
367         "    <separator />"
368         "    <toolitem action='CapRoundingAction' />"
369         "    <separator />"
370         "    <toolitem action='TremorAction' />"
371         "    <toolitem action='WiggleAction' />"
372         "    <toolitem action='MassAction' />"
373         "    <separator />"
374         "  </toolbar>"
376         "  <toolbar name='ArcToolbar'>"
377         "    <toolitem action='ArcStateAction' />"
378         "    <separator />"
379         "    <toolitem action='ArcStartAction' />"
380         "    <toolitem action='ArcEndAction' />"
381         "    <separator />"
382         "    <toolitem action='ArcOpenAction' />"
383         "    <separator />"
384         "    <toolitem action='ArcResetAction' />"
385         "    <separator />"
386         "  </toolbar>"
388         "  <toolbar name='PaintbucketToolbar'>"
389         "    <toolitem action='ChannelsAction' />"
390         "    <separator />"
391         "    <toolitem action='ThresholdAction' />"
392         "    <separator />"
393         "    <toolitem action='OffsetAction' />"
394         "    <toolitem action='PaintbucketUnitsAction' />"
395         "    <separator />"
396         "    <toolitem action='AutoGapAction' />"
397         "    <separator />"
398         "    <toolitem action='PaintbucketResetAction' />"
399         "  </toolbar>"
401         "  <toolbar name='DropperToolbar'>"
402         "    <toolitem action='DropperOpacityAction' />"
403         "    <toolitem action='DropperPickAlphaAction' />"
404         "    <toolitem action='DropperSetAlphaAction' />"
405         "  </toolbar>"
407         "  <toolbar name='ConnectorToolbar'>"
408         "    <toolitem action='ConnectorAvoidAction' />"
409         "    <toolitem action='ConnectorIgnoreAction' />"
410         "    <toolitem action='ConnectorSpacingAction' />"
411         "    <toolitem action='ConnectorGraphAction' />"
412         "    <toolitem action='ConnectorLengthAction' />"
413         "    <toolitem action='ConnectorDirectedAction' />"
414         "    <toolitem action='ConnectorOverlapAction' />"
415         "  </toolbar>"
417         "</ui>"
420 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
422 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
424 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
425 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
427 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
428 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
430 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
431 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
433 /* Global text entry widgets necessary for update */
434 /* GtkWidget *dropper_rgb_entry,
435           *dropper_opacity_entry ; */
436 // should be made a private member once this is converted to class
438 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
439     connection->disconnect();
440     delete connection;
443 static void purge_repr_listener( GObject* obj, GObject* tbl )
445     (void)obj;
446     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
447     if (oldrepr) { // remove old listener
448         sp_repr_remove_listener_by_data(oldrepr, tbl);
449         Inkscape::GC::release(oldrepr);
450         oldrepr = 0;
451         g_object_set_data( tbl, "repr", NULL );
452     }
455 GtkWidget *
456 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
457                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
458                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
460     SPAction *action = verb->get_action(view);
461     if (!action) return NULL;
463     SPAction *doubleclick_action;
464     if (doubleclick_verb)
465         doubleclick_action = doubleclick_verb->get_action(view);
466     else
467         doubleclick_action = NULL;
469     /* fixme: Handle sensitive/unsensitive */
470     /* fixme: Implement sp_button_new_from_action */
471     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
472     gtk_widget_show(b);
473     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
475     return b;
478 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
479                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
481     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
484 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
485                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
487     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
491 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
493     SPAction* targetAction = SP_ACTION(user_data);
494     if ( targetAction ) {
495         sp_action_perform( targetAction, NULL );
496     }
499 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
501     if ( data ) {
502         GtkAction* act = GTK_ACTION(data);
503         gtk_action_set_sensitive( act, sensitive );
504     }
507 static SPActionEventVector action_event_vector = {
508     {NULL},
509     NULL,
510     NULL,
511     sp_action_action_set_sensitive,
512     NULL,
513     NULL
514 };
516 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
518     GtkAction* act = 0;
520     SPAction* targetAction = verb->get_action(view);
521     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
522     act = GTK_ACTION(inky);
523     gtk_action_set_sensitive( act, targetAction->sensitive );
525     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
527     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
528     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
530     return act;
533 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
535     Inkscape::UI::View::View *view = desktop;
536     gint verbsToUse[] = {
537         // disabled until we have icons for them:
538         //find
539         //SP_VERB_EDIT_TILE,
540         //SP_VERB_EDIT_UNTILE,
541         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
542         SP_VERB_DIALOG_DISPLAY,
543         SP_VERB_DIALOG_FILL_STROKE,
544         SP_VERB_DIALOG_NAMEDVIEW,
545         SP_VERB_DIALOG_TEXT,
546         SP_VERB_DIALOG_XML_EDITOR,
547         SP_VERB_EDIT_CLONE,
548         SP_VERB_EDIT_COPY,
549         SP_VERB_EDIT_CUT,
550         SP_VERB_EDIT_DUPLICATE,
551         SP_VERB_EDIT_PASTE,
552         SP_VERB_EDIT_REDO,
553         SP_VERB_EDIT_UNDO,
554         SP_VERB_EDIT_UNLINK_CLONE,
555         SP_VERB_FILE_EXPORT,
556         SP_VERB_FILE_IMPORT,
557         SP_VERB_FILE_NEW,
558         SP_VERB_FILE_OPEN,
559         SP_VERB_FILE_PRINT,
560         SP_VERB_FILE_SAVE,
561         SP_VERB_OBJECT_TO_CURVE,
562         SP_VERB_SELECTION_GROUP,
563         SP_VERB_SELECTION_OUTLINE,
564         SP_VERB_SELECTION_UNGROUP,
565         SP_VERB_ZOOM_1_1,
566         SP_VERB_ZOOM_1_2,
567         SP_VERB_ZOOM_2_1,
568         SP_VERB_ZOOM_DRAWING,
569         SP_VERB_ZOOM_IN,
570         SP_VERB_ZOOM_NEXT,
571         SP_VERB_ZOOM_OUT,
572         SP_VERB_ZOOM_PAGE,
573         SP_VERB_ZOOM_PAGE_WIDTH,
574         SP_VERB_ZOOM_PREV,
575         SP_VERB_ZOOM_SELECTION,
576     };
578     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
579     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
581     static std::map<SPDesktop*, GtkActionGroup*> groups;
582     GtkActionGroup* mainActions = 0;
583     if ( groups.find(desktop) != groups.end() ) {
584         mainActions = groups[desktop];
585     }
587     if ( !mainActions ) {
588         mainActions = gtk_action_group_new("main");
589         groups[desktop] = mainActions;
590     }
592     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
593         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
594         if ( verb ) {
595             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
596                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
597                 gtk_action_group_add_action( mainActions, act );
598             }
599         }
600     }
602     return mainActions;
606 GtkWidget *
607 sp_tool_toolbox_new()
609     GtkTooltips *tt = gtk_tooltips_new();
610     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
612     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
613     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
615     gtk_widget_set_sensitive(tb, FALSE);
617     GtkWidget *hb = gtk_handle_box_new();
618     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
619     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
620     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
622     gtk_container_add(GTK_CONTAINER(hb), tb);
623     gtk_widget_show(GTK_WIDGET(tb));
625     sigc::connection* conn = new sigc::connection;
626     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
628     return hb;
631 static void
632 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
634     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
635     gtk_widget_queue_resize(child);
638 static void
639 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
641     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
642     gtk_widget_queue_resize(child);
645 GtkWidget *
646 sp_aux_toolbox_new()
648     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
650     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
652     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;
673 //####################################
674 //# Commands Bar
675 //####################################
677 GtkWidget *
678 sp_commands_toolbox_new()
680     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
682     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
684     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
685     gtk_widget_set_sensitive(tb, FALSE);
687     GtkWidget *hb = gtk_handle_box_new();
688     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
689     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
690     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
692     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
693     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
695     gtk_container_add(GTK_CONTAINER(hb), tb);
696     gtk_widget_show(GTK_WIDGET(tb));
698     sigc::connection* conn = new sigc::connection;
699     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
701     return hb;
704 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
705                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
706                                                        gchar const *path, gchar const *data, gdouble def,
707                                                        GtkWidget *focusTarget,
708                                                        GtkWidget *us,
709                                                        GObject *dataKludge,
710                                                        gboolean altx, gchar const *altx_mark,
711                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
712                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
713                                                        void (*callback)(GtkAdjustment *, GObject *),
714                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
716     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
717                                                              lower, upper, step, page, page ) );
718     if (us) {
719         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
720     }
722     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
724     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
725     if ( shortLabel ) {
726         g_object_set( act, "short_label", shortLabel, NULL );
727     }
729     if ( (descrCount > 0) && descrLabels && descrValues ) {
730         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
731     }
733     if ( focusTarget ) {
734         ege_adjustment_action_set_focuswidget( act, focusTarget );
735     }
737     if ( altx && altx_mark ) {
738         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
739     }
741     if ( dataKludge ) {
742         g_object_set_data( dataKludge, data, adj );
743     }
745     // Using a cast just to make sure we pass in the right kind of function pointer
746     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
748     return act;
752 //####################################
753 //# node editing callbacks
754 //####################################
756 /**
757  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
758  */
759 static ShapeEditor *get_current_shape_editor()
761     if (!SP_ACTIVE_DESKTOP) {
762         return NULL;
763     }
765     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
767     if (!SP_IS_NODE_CONTEXT(event_context)) {
768         return NULL;
769     }
771     return SP_NODE_CONTEXT(event_context)->shape_editor;
775 void
776 sp_node_path_edit_add(void)
778     ShapeEditor *shape_editor = get_current_shape_editor();
779     if (shape_editor) shape_editor->add_node();
782 void
783 sp_node_path_edit_delete(void)
785     ShapeEditor *shape_editor = get_current_shape_editor();
786     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
789 void
790 sp_node_path_edit_delete_segment(void)
792     ShapeEditor *shape_editor = get_current_shape_editor();
793     if (shape_editor) shape_editor->delete_segment();
796 void
797 sp_node_path_edit_break(void)
799     ShapeEditor *shape_editor = get_current_shape_editor();
800     if (shape_editor) shape_editor->break_at_nodes();
803 void
804 sp_node_path_edit_join(void)
806     ShapeEditor *shape_editor = get_current_shape_editor();
807     if (shape_editor) shape_editor->join_nodes();
810 void
811 sp_node_path_edit_join_segment(void)
813     ShapeEditor *shape_editor = get_current_shape_editor();
814     if (shape_editor) shape_editor->join_segments();
817 void
818 sp_node_path_edit_toline(void)
820     ShapeEditor *shape_editor = get_current_shape_editor();
821     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
824 void
825 sp_node_path_edit_tocurve(void)
827     ShapeEditor *shape_editor = get_current_shape_editor();
828     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
831 void
832 sp_node_path_edit_cusp(void)
834     ShapeEditor *shape_editor = get_current_shape_editor();
835     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
838 void
839 sp_node_path_edit_smooth(void)
841     ShapeEditor *shape_editor = get_current_shape_editor();
842     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
845 void
846 sp_node_path_edit_symmetrical(void)
848     ShapeEditor *shape_editor = get_current_shape_editor();
849     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
852 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
853     bool show = gtk_toggle_action_get_active( act );
854     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
855     ShapeEditor *shape_editor = get_current_shape_editor();
856     if (shape_editor) shape_editor->show_handles(show);
859 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
860     bool show = gtk_toggle_action_get_active( act );
861     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
862     ShapeEditor *shape_editor = get_current_shape_editor();
863     if (shape_editor) shape_editor->show_helperpath(show);
866 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
867     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
870 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
871     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
874 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
875     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
878 /* is called when the node selection is modified */
879 static void
880 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
882     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
883     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
884     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
885     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
887     // quit if run by the attr_changed listener
888     if (g_object_get_data( tbl, "freeze" )) {
889         return;
890     }
892     // in turn, prevent listener from responding
893     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
895     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
896     SPUnit const *unit = tracker->getActiveUnit();
898     ShapeEditor *shape_editor = get_current_shape_editor();
899     if (shape_editor && shape_editor->has_nodepath()) {
900         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
901         int n_selected = 0;
902         if (nodepath) {
903             n_selected = nodepath->numSelected();
904         }
906         if (n_selected == 0) {
907             gtk_action_set_sensitive(xact, FALSE);
908             gtk_action_set_sensitive(yact, FALSE);
909         } else {
910             gtk_action_set_sensitive(xact, TRUE);
911             gtk_action_set_sensitive(yact, TRUE);
912             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
913             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
915             if (n_selected == 1) {
916                 NR::Point sel_node = nodepath->singleSelectedCoords();
917                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
918                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
919                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
920                 }
921             } else {
922                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
923                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
924                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
925                     /* Note: Currently x and y will always have a value, even if the coordinates of the
926                        selected nodes don't coincide (in this case we use the coordinates of the center
927                        of the bounding box). So the entries are never set to zero. */
928                     // FIXME: Maybe we should clear the entry if several nodes are selected
929                     //        instead of providing a kind of average value
930                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
931                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
932                 }
933             }
934         }
935     } else {
936         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
937         gtk_action_set_sensitive(xact, FALSE);
938         gtk_action_set_sensitive(yact, FALSE);
939     }
941     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
944 static void
945 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
947     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
949     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
950     SPUnit const *unit = tracker->getActiveUnit();
952     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
953         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
954     }
956     // quit if run by the attr_changed listener
957     if (g_object_get_data( tbl, "freeze" )) {
958         return;
959     }
961     // in turn, prevent listener from responding
962     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
964     ShapeEditor *shape_editor = get_current_shape_editor();
965     if (shape_editor && shape_editor->has_nodepath()) {
966         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
967         if (!strcmp(value_name, "x")) {
968             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
969         }
970         if (!strcmp(value_name, "y")) {
971             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
972         }
973     }
975     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
978 static void
979 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
981     sp_node_path_value_changed(adj, tbl, "x");
984 static void
985 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
987     sp_node_path_value_changed(adj, tbl, "y");
990 void
991 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
993     {
994     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
995     SPItem *item = selection->singleItem();
996     if (item && SP_IS_LPE_ITEM(item)) {
997        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
998            gtk_action_set_sensitive(w, TRUE);
999        } else {
1000            gtk_action_set_sensitive(w, FALSE);
1001        }
1002     } else {
1003        gtk_action_set_sensitive(w, FALSE);
1004     }
1005     }
1007     {
1008     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1009     SPItem *item = selection->singleItem();
1010     if (item && item->clip_ref && item->clip_ref->getObject()) {
1011        gtk_action_set_sensitive(w, TRUE);
1012     } else {
1013        gtk_action_set_sensitive(w, FALSE);
1014     }
1015     }
1017     {
1018     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1019     SPItem *item = selection->singleItem();
1020     if (item && item->mask_ref && item->mask_ref->getObject()) {
1021        gtk_action_set_sensitive(w, TRUE);
1022     } else {
1023        gtk_action_set_sensitive(w, FALSE);
1024     }
1025     }
1028 void
1029 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1031     sp_node_toolbox_sel_changed (selection, tbl);
1036 //################################
1037 //##    Node Editing Toolbox    ##
1038 //################################
1040 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1042     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1043     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1044     g_object_set_data( holder, "tracker", tracker );
1046     {
1047         InkAction* inky = ink_action_new( "NodeInsertAction",
1048                                           _("Insert node"),
1049                                           _("Insert new nodes into selected segments"),
1050                                           "node_insert",
1051                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1052         g_object_set( inky, "short_label", _("Insert"), NULL );
1053         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1054         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1055     }
1057     {
1058         InkAction* inky = ink_action_new( "NodeDeleteAction",
1059                                           _("Delete node"),
1060                                           _("Delete selected nodes"),
1061                                           "node_delete",
1062                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1063         g_object_set( inky, "short_label", _("Delete"), NULL );
1064         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1065         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1066     }
1068     {
1069         InkAction* inky = ink_action_new( "NodeJoinAction",
1070                                           _("Join endnodes"),
1071                                           _("Join selected endnodes"),
1072                                           "node_join",
1073                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1074         g_object_set( inky, "short_label", _("Join"), NULL );
1075         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1076         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1077     }
1079     {
1080         InkAction* inky = ink_action_new( "NodeBreakAction",
1081                                           _("Break nodes"),
1082                                           _("Break path at selected nodes"),
1083                                           "node_break",
1084                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1085         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1086         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1087     }
1090     {
1091         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1092                                           _("Join with segment"),
1093                                           _("Join selected endnodes with a new segment"),
1094                                           "node_join_segment",
1095                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1096         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1097         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1098     }
1100     {
1101         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1102                                           _("Delete segment"),
1103                                           _("Delete segment between two non-endpoint nodes"),
1104                                           "node_delete_segment",
1105                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1106         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1107         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1108     }
1110     {
1111         InkAction* inky = ink_action_new( "NodeCuspAction",
1112                                           _("Node Cusp"),
1113                                           _("Make selected nodes corner"),
1114                                           "node_cusp",
1115                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1116         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1117         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1118     }
1120     {
1121         InkAction* inky = ink_action_new( "NodeSmoothAction",
1122                                           _("Node Smooth"),
1123                                           _("Make selected nodes smooth"),
1124                                           "node_smooth",
1125                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1126         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1127         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1128     }
1130     {
1131         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1132                                           _("Node Symmetric"),
1133                                           _("Make selected nodes symmetric"),
1134                                           "node_symmetric",
1135                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1136         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1137         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1138     }
1140     {
1141         InkAction* inky = ink_action_new( "NodeLineAction",
1142                                           _("Node Line"),
1143                                           _("Make selected segments lines"),
1144                                           "node_line",
1145                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1146         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1147         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1148     }
1150     {
1151         InkAction* inky = ink_action_new( "NodeCurveAction",
1152                                           _("Node Curve"),
1153                                           _("Make selected segments curves"),
1154                                           "node_curve",
1155                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1156         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1157         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1158     }
1160     {
1161         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1162                                                       _("Show Handles"),
1163                                                       _("Show the Bezier handles of selected nodes"),
1164                                                       "nodes_show_handles",
1165                                                       Inkscape::ICON_SIZE_DECORATION );
1166         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1167         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1168         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1169     }
1171     {
1172         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1173                                                       _("Show Outline"),
1174                                                       _("Show the outline of the path"),
1175                                                       "nodes_show_helperpath",
1176                                                       Inkscape::ICON_SIZE_DECORATION );
1177         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1178         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1179         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1180     }
1182     {
1183         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1184                                           _("Next path effect parameter"),
1185                                           _("Show next path effect parameter for editing"),
1186                                           "edit_next_parameter",
1187                                           Inkscape::ICON_SIZE_DECORATION );
1188         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1189         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1190         g_object_set_data( holder, "nodes_lpeedit", inky);
1191     }
1193     {
1194         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1195                                           _("Edit clipping path"),
1196                                           _("Edit the clipping path of the object"),
1197                                           "nodeedit-clippath",
1198                                           Inkscape::ICON_SIZE_DECORATION );
1199         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1200         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1201         g_object_set_data( holder, "nodes_clippathedit", inky);
1202     }
1204     {
1205         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1206                                           _("Edit mask path"),
1207                                           _("Edit the mask of the object"),
1208                                           "nodeedit-mask",
1209                                           Inkscape::ICON_SIZE_DECORATION );
1210         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1211         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1212         g_object_set_data( holder, "nodes_maskedit", inky);
1213     }
1215     /* X coord of selected node(s) */
1216     {
1217         EgeAdjustmentAction* eact = 0;
1218         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1219         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1220         eact = create_adjustment_action( "NodeXAction",
1221                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1222                                          "tools.nodes", "Xcoord", 0,
1223                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1224                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1225                                          labels, values, G_N_ELEMENTS(labels),
1226                                          sp_node_path_x_value_changed );
1227         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1228         g_object_set_data( holder, "nodes_x_action", eact );
1229         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1230         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1231     }
1233     /* Y coord of selected node(s) */
1234     {
1235         EgeAdjustmentAction* eact = 0;
1236         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1237         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1238         eact = create_adjustment_action( "NodeYAction",
1239                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1240                                          "tools.nodes", "Ycoord", 0,
1241                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1242                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1243                                          labels, values, G_N_ELEMENTS(labels),
1244                                          sp_node_path_y_value_changed );
1245         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1246         g_object_set_data( holder, "nodes_y_action", eact );
1247         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1248         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1249     }
1251     // add the units menu
1252     {
1253         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1254         gtk_action_group_add_action( mainActions, act );
1255     }
1258     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1260     //watch selection
1261     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1263     sigc::connection *c_selection_changed =
1264         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1265                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1266     pool->add_connection ("selection-changed", c_selection_changed);
1268     sigc::connection *c_selection_modified =
1269         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1270                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1271     pool->add_connection ("selection-modified", c_selection_modified);
1273     sigc::connection *c_subselection_changed =
1274         new sigc::connection (desktop->connectToolSubselectionChanged
1275                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1276     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1278     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1280     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1281 } // end of sp_node_toolbox_prep()
1284 //########################
1285 //##    Zoom Toolbox    ##
1286 //########################
1288 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1290     // no custom GtkAction setup needed
1291 } // end of sp_zoom_toolbox_prep()
1293 void
1294 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1296     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")));
1300 void
1301 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1303     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")));
1306 void
1307 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1309     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")));
1312 static void
1313 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1315     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1316     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1318     if (old_desktop) {
1319         GList *children, *iter;
1321         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1322         for ( iter = children ; iter ; iter = iter->next ) {
1323             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1324         }
1325         g_list_free(children);
1326     }
1328     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1330     if (desktop) {
1331         gtk_widget_set_sensitive(toolbox, TRUE);
1332         setup_func(toolbox, desktop);
1333         update_func(desktop, desktop->event_context, toolbox);
1334         *conn = desktop->connectEventContextChanged
1335             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1336     } else {
1337         gtk_widget_set_sensitive(toolbox, FALSE);
1338     }
1340 } // end of toolbox_set_desktop()
1343 static void
1344 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1346     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1347     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1348     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1349         // "toolbox.tools" was not set. Fallback to older value
1350         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1352         // Copy the setting forwards
1353         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1354     }
1355     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1357     for (int i = 0 ; tools[i].type_name ; i++ ) {
1358         GtkWidget *button =
1359             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1360                                                               SP_BUTTON_TYPE_TOGGLE,
1361                                                               Inkscape::Verb::get(tools[i].verb),
1362                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1363                                                               desktop,
1364                                                               tooltips );
1366         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1367                            (gpointer)button );
1368     }
1372 static void
1373 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1375     gchar const *const tname = ( eventcontext
1376                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1377                                  : NULL );
1378     for (int i = 0 ; tools[i].type_name ; i++ ) {
1379         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1380         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1381     }
1384 static void
1385 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1387     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1388     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1389     GtkUIManager* mgr = gtk_ui_manager_new();
1390     GError* errVal = 0;
1391     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1392     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1394     std::map<std::string, GtkWidget*> dataHolders;
1396     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1397         if ( aux_toolboxes[i].prep_func ) {
1398             // converted to GtkActions and UIManager
1400             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1401             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1402             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1403             dataHolders[aux_toolboxes[i].type_name] = kludge;
1404             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1405         } else {
1407             GtkWidget *sub_toolbox = 0;
1408             if (aux_toolboxes[i].create_func == NULL)
1409                 sub_toolbox = sp_empty_toolbox_new(desktop);
1410             else {
1411                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1412             }
1414             gtk_size_group_add_widget( grouper, sub_toolbox );
1416             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1417             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1419         }
1420     }
1422     // Second pass to create toolbars *after* all GtkActions are created
1423     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1424         if ( aux_toolboxes[i].prep_func ) {
1425             // converted to GtkActions and UIManager
1427             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1429             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1430             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1432             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1433             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1434             g_free( tmp );
1435             tmp = 0;
1437             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1438             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1439             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1440                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1441             }
1442             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1445             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1447             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1448                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1449                 swatch->setDesktop( desktop );
1450                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1451                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1452                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1453                 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 );
1454             }
1456             gtk_widget_show_all( holder );
1457             sp_set_font_size_smaller( holder );
1459             gtk_size_group_add_widget( grouper, holder );
1461             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1462             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1463         }
1464     }
1466     g_object_unref( G_OBJECT(grouper) );
1469 static void
1470 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1472     gchar const *tname = ( eventcontext
1473                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1474                            : NULL );
1475     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1476         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1477         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1478             gtk_widget_show_all(sub_toolbox);
1479             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1480         } else {
1481             gtk_widget_hide(sub_toolbox);
1482         }
1483     }
1486 static void
1487 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1489     gchar const * descr =
1490         "<ui>"
1491         "  <toolbar name='CommandsToolbar'>"
1492         "    <toolitem action='FileNew' />"
1493         "    <toolitem action='FileOpen' />"
1494         "    <toolitem action='FileSave' />"
1495         "    <toolitem action='FilePrint' />"
1496         "    <separator />"
1497         "    <toolitem action='FileImport' />"
1498         "    <toolitem action='FileExport' />"
1499         "    <separator />"
1500         "    <toolitem action='EditUndo' />"
1501         "    <toolitem action='EditRedo' />"
1502         "    <separator />"
1503         "    <toolitem action='EditCopy' />"
1504         "    <toolitem action='EditCut' />"
1505         "    <toolitem action='EditPaste' />"
1506         "    <separator />"
1507         "    <toolitem action='ZoomSelection' />"
1508         "    <toolitem action='ZoomDrawing' />"
1509         "    <toolitem action='ZoomPage' />"
1510         "    <separator />"
1511         "    <toolitem action='EditDuplicate' />"
1512         "    <toolitem action='EditClone' />"
1513         "    <toolitem action='EditUnlinkClone' />"
1514         "    <separator />"
1515         "    <toolitem action='SelectionGroup' />"
1516         "    <toolitem action='SelectionUnGroup' />"
1517         "    <separator />"
1518         "    <toolitem action='DialogFillStroke' />"
1519         "    <toolitem action='DialogText' />"
1520         "    <toolitem action='DialogXMLEditor' />"
1521         "    <toolitem action='DialogAlignDistribute' />"
1522         "    <separator />"
1523         "    <toolitem action='DialogPreferences' />"
1524         "    <toolitem action='DialogDocumentProperties' />"
1525         "  </toolbar>"
1526         "</ui>";
1527     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1530     GtkUIManager* mgr = gtk_ui_manager_new();
1531     GError* errVal = 0;
1533     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1534     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1536     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1537     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1538         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1539     }
1540     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1541     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1542     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1545     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1548 static void
1549 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1553 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1555     gtk_widget_show(toolbox_toplevel);
1556     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1558     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1559     if (!shown_toolbox) {
1560         return;
1561     }
1562     gtk_widget_show(toolbox);
1564     gtk_widget_show_all(shown_toolbox);
1567 void
1568 aux_toolbox_space(GtkWidget *tb, gint space)
1570     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1573 static GtkWidget *
1574 sp_empty_toolbox_new(SPDesktop *desktop)
1576     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1577     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1578     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1580     gtk_widget_show_all(tbl);
1581     sp_set_font_size_smaller (tbl);
1583     return tbl;
1586 // helper UI functions
1588 GtkWidget *
1589 sp_tb_spinbutton(
1590     gchar *label, gchar const *tooltip,
1591     gchar const *path, gchar const *data, gdouble def,
1592     GtkWidget *us,
1593     GtkWidget *tbl,
1594     gboolean altx, gchar const *altx_mark,
1595     gdouble lower, gdouble upper, gdouble step, gdouble page,
1596     void (*callback)(GtkAdjustment *, GtkWidget *),
1597     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1599     GtkTooltips *tt = gtk_tooltips_new();
1601     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1603     GtkWidget *l = gtk_label_new(label);
1604     gtk_widget_show(l);
1605     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1606     gtk_container_add(GTK_CONTAINER(hb), l);
1608     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1609                                       lower, upper, step, page, page);
1610     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1611     if (us)
1612         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1614     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1615     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1616     if (altx)
1617         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1618     gtk_widget_set_size_request(sb,
1619                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1620                                 AUX_SPINBUTTON_HEIGHT);
1621     gtk_widget_show(sb);
1622     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1623     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1624     gtk_container_add(GTK_CONTAINER(hb), sb);
1625     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1627     return hb;
1630 #define MODE_LABEL_WIDTH 70
1632 //########################
1633 //##       Star         ##
1634 //########################
1636 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1638     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1640     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1641         // do not remember prefs if this call is initiated by an undo change, because undoing object
1642         // creation sets bogus values to its attributes before it is deleted
1643         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1644     }
1646     // quit if run by the attr_changed listener
1647     if (g_object_get_data( dataKludge, "freeze" )) {
1648         return;
1649     }
1651     // in turn, prevent listener from responding
1652     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1654     bool modmade = false;
1656     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1657     GSList const *items = selection->itemList();
1658     for (; items != NULL; items = items->next) {
1659         if (SP_IS_STAR((SPItem *) items->data)) {
1660             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1661             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1662             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1663                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1664                                     + M_PI / (gint)adj->value));
1665             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1666             modmade = true;
1667         }
1668     }
1669     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1670                                    _("Star: Change number of corners"));
1672     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1675 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1677     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1679     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1680         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1681     }
1683     // quit if run by the attr_changed listener
1684     if (g_object_get_data( dataKludge, "freeze" )) {
1685         return;
1686     }
1688     // in turn, prevent listener from responding
1689     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1691     bool modmade = false;
1692     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1693     GSList const *items = selection->itemList();
1694     for (; items != NULL; items = items->next) {
1695         if (SP_IS_STAR((SPItem *) items->data)) {
1696             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1698             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1699             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1700             if (r2 < r1) {
1701                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1702             } else {
1703                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1704             }
1706             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1707             modmade = true;
1708         }
1709     }
1711     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1712                                    _("Star: Change spoke ratio"));
1714     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1717 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1719     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1720     bool flat = ege_select_one_action_get_active( act ) == 0;
1722     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1723         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1724                                     flat ? "true" : "false" );
1725     }
1727     // quit if run by the attr_changed listener
1728     if (g_object_get_data( dataKludge, "freeze" )) {
1729         return;
1730     }
1732     // in turn, prevent listener from responding
1733     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1735     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1736     GSList const *items = selection->itemList();
1737     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1738     bool modmade = false;
1740     if ( prop_action ) {
1741         gtk_action_set_sensitive( prop_action, !flat );
1742     }
1744     for (; items != NULL; items = items->next) {
1745         if (SP_IS_STAR((SPItem *) items->data)) {
1746             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1747             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1748             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1749             modmade = true;
1750         }
1751     }
1753     if (modmade) {
1754         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1755                          flat ? _("Make polygon") : _("Make star"));
1756     }
1758     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1761 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1763     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1765     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1766         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1767     }
1769     // quit if run by the attr_changed listener
1770     if (g_object_get_data( dataKludge, "freeze" )) {
1771         return;
1772     }
1774     // in turn, prevent listener from responding
1775     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1777     bool modmade = false;
1779     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1780     GSList const *items = selection->itemList();
1781     for (; items != NULL; items = items->next) {
1782         if (SP_IS_STAR((SPItem *) items->data)) {
1783             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1784             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1785             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1786             modmade = true;
1787         }
1788     }
1789     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1790                                    _("Star: Change rounding"));
1792     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1795 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1797     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1799     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1800         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1801     }
1803     // quit if run by the attr_changed listener
1804     if (g_object_get_data( dataKludge, "freeze" )) {
1805         return;
1806     }
1808     // in turn, prevent listener from responding
1809     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1811     bool modmade = false;
1813     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1814     GSList const *items = selection->itemList();
1815     for (; items != NULL; items = items->next) {
1816         if (SP_IS_STAR((SPItem *) items->data)) {
1817             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1818             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1819             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1820             modmade = true;
1821         }
1822     }
1823     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1824                                    _("Star: Change randomization"));
1826     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1830 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1831                                        gchar const */*old_value*/, gchar const */*new_value*/,
1832                                        bool /*is_interactive*/, gpointer data)
1834     GtkWidget *tbl = GTK_WIDGET(data);
1836     // quit if run by the _changed callbacks
1837     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1838         return;
1839     }
1841     // in turn, prevent callbacks from responding
1842     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1844     GtkAdjustment *adj = 0;
1846     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1847     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1849     if (!strcmp(name, "inkscape:randomized")) {
1850         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1851         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1852     } else if (!strcmp(name, "inkscape:rounded")) {
1853         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1854         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1855     } else if (!strcmp(name, "inkscape:flatsided")) {
1856         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1857         char const *flatsides = repr->attribute("inkscape:flatsided");
1858         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1859         if ( flatsides && !strcmp(flatsides,"false") ) {
1860             ege_select_one_action_set_active( flat_action, 1 );
1861             gtk_action_set_sensitive( prop_action, TRUE );
1862         } else {
1863             ege_select_one_action_set_active( flat_action, 0 );
1864             gtk_action_set_sensitive( prop_action, FALSE );
1865         }
1866     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1867         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1868         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1869         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1870         if (r2 < r1) {
1871             gtk_adjustment_set_value(adj, r2/r1);
1872         } else {
1873             gtk_adjustment_set_value(adj, r1/r2);
1874         }
1875     } else if (!strcmp(name, "sodipodi:sides")) {
1876         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1877         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1878     }
1880     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1884 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1886     NULL, /* child_added */
1887     NULL, /* child_removed */
1888     star_tb_event_attr_changed,
1889     NULL, /* content_changed */
1890     NULL  /* order_changed */
1891 };
1894 /**
1895  *  \param selection Should not be NULL.
1896  */
1897 static void
1898 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1900     int n_selected = 0;
1901     Inkscape::XML::Node *repr = NULL;
1903     purge_repr_listener( tbl, tbl );
1905     for (GSList const *items = selection->itemList();
1906          items != NULL;
1907          items = items->next)
1908     {
1909         if (SP_IS_STAR((SPItem *) items->data)) {
1910             n_selected++;
1911             repr = SP_OBJECT_REPR((SPItem *) items->data);
1912         }
1913     }
1915     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1917     if (n_selected == 0) {
1918         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1919     } else if (n_selected == 1) {
1920         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1922         if (repr) {
1923             g_object_set_data( tbl, "repr", repr );
1924             Inkscape::GC::anchor(repr);
1925             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1926             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1927         }
1928     } else {
1929         // FIXME: implement averaging of all parameters for multiple selected stars
1930         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1931         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1932     }
1936 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1938     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1939     // callbacks to lump all the changes for all selected objects in one undo step
1941     GtkAdjustment *adj = 0;
1943     // fixme: make settable in prefs!
1944     gint mag = 5;
1945     gdouble prop = 0.5;
1946     gboolean flat = FALSE;
1947     gdouble randomized = 0;
1948     gdouble rounded = 0;
1950     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1951     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1953     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1954     gtk_action_set_sensitive( sb2, !flat );
1956     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1957     gtk_adjustment_set_value(adj, mag);
1958     gtk_adjustment_value_changed(adj);
1960     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1961     gtk_adjustment_set_value(adj, prop);
1962     gtk_adjustment_value_changed(adj);
1964     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1965     gtk_adjustment_set_value(adj, rounded);
1966     gtk_adjustment_value_changed(adj);
1968     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1969     gtk_adjustment_set_value(adj, randomized);
1970     gtk_adjustment_value_changed(adj);
1974 void
1975 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1977     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1978     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1979     GtkWidget *l = gtk_label_new(NULL);
1980     gtk_label_set_markup(GTK_LABEL(l), title);
1981     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1982     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1983     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1987 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1989     {
1990         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1991         ege_output_action_set_use_markup( act, TRUE );
1992         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1993         g_object_set_data( holder, "mode_action", act );
1994     }
1996     {
1997         EgeAdjustmentAction* eact = 0;
1998         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1999         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2001         /* Flatsided checkbox */
2002         {
2003             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2005             GtkTreeIter iter;
2006             gtk_list_store_append( model, &iter );
2007             gtk_list_store_set( model, &iter,
2008                                 0, _("Polygon"),
2009                                 1, _("Regular polygon (with one handle) instead of a star"),
2010                                 2, "star_flat",
2011                                 -1 );
2013             gtk_list_store_append( model, &iter );
2014             gtk_list_store_set( model, &iter,
2015                                 0, _("Star"),
2016                                 1, _("Star instead of a regular polygon (with one handle)"),
2017                                 2, "star_angled",
2018                                 -1 );
2020             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2021             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2022             g_object_set_data( holder, "flat_action", act );
2024             ege_select_one_action_set_appearance( act, "full" );
2025             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2026             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2027             ege_select_one_action_set_icon_column( act, 2 );
2028             ege_select_one_action_set_tooltip_column( act, 1  );
2030             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2031             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2032         }
2034         /* Magnitude */
2035         {
2036         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2037         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2038         eact = create_adjustment_action( "MagnitudeAction",
2039                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2040                                          "tools.shapes.star", "magnitude", 3,
2041                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2042                                          3, 1024, 1, 5,
2043                                          labels, values, G_N_ELEMENTS(labels),
2044                                          sp_stb_magnitude_value_changed,
2045                                          1.0, 0 );
2046         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2047         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2048         }
2050         /* Spoke ratio */
2051         {
2052         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2053         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2054         eact = create_adjustment_action( "SpokeAction",
2055                                          _("Spoke ratio"), _("Spoke ratio:"),
2056                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2057                                          // Base radius is the same for the closest handle.
2058                                          _("Base radius to tip radius ratio"),
2059                                          "tools.shapes.star", "proportion", 0.5,
2060                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2061                                          0.01, 1.0, 0.01, 0.1,
2062                                          labels, values, G_N_ELEMENTS(labels),
2063                                          sp_stb_proportion_value_changed );
2064         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2065         g_object_set_data( holder, "prop_action", eact );
2066         }
2068         if ( !isFlatSided ) {
2069             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2070         } else {
2071             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2072         }
2074         /* Roundedness */
2075         {
2076         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2077         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2078         eact = create_adjustment_action( "RoundednessAction",
2079                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2080                                          "tools.shapes.star", "rounded", 0.0,
2081                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2082                                          -10.0, 10.0, 0.01, 0.1,
2083                                          labels, values, G_N_ELEMENTS(labels),
2084                                          sp_stb_rounded_value_changed );
2085         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2086         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2087         }
2089         /* Randomization */
2090         {
2091         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2092         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2093         eact = create_adjustment_action( "RandomizationAction",
2094                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2095                                          "tools.shapes.star", "randomized", 0.0,
2096                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2097                                          -10.0, 10.0, 0.001, 0.01,
2098                                          labels, values, G_N_ELEMENTS(labels),
2099                                          sp_stb_randomized_value_changed, 0.1, 3 );
2100         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2101         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2102         }
2103     }
2105     {
2106         /* Reset */
2107         {
2108             GtkAction* act = gtk_action_new( "StarResetAction",
2109                                              _("Defaults"),
2110                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2111                                              GTK_STOCK_CLEAR );
2112             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2113             gtk_action_group_add_action( mainActions, act );
2114             gtk_action_set_sensitive( act, TRUE );
2115         }
2116     }
2118     sigc::connection *connection = new sigc::connection(
2119         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2120         );
2121     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2122     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2126 //########################
2127 //##       Rect         ##
2128 //########################
2130 static void sp_rtb_sensitivize( GObject *tbl )
2132     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2133     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2134     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2136     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2137         gtk_action_set_sensitive( not_rounded, FALSE );
2138     } else {
2139         gtk_action_set_sensitive( not_rounded, TRUE );
2140     }
2144 static void
2145 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2146                           void (*setter)(SPRect *, gdouble))
2148     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2150     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2151     SPUnit const *unit = tracker->getActiveUnit();
2153     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2154         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2155     }
2157     // quit if run by the attr_changed listener
2158     if (g_object_get_data( tbl, "freeze" )) {
2159         return;
2160     }
2162     // in turn, prevent listener from responding
2163     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2165     bool modmade = false;
2166     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2167     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2168         if (SP_IS_RECT(items->data)) {
2169             if (adj->value != 0) {
2170                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2171             } else {
2172                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2173             }
2174             modmade = true;
2175         }
2176     }
2178     sp_rtb_sensitivize( tbl );
2180     if (modmade) {
2181         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2182                                    _("Change rectangle"));
2183     }
2185     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2188 static void
2189 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2191     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2194 static void
2195 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2197     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2200 static void
2201 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2203     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2206 static void
2207 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2209     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2214 static void
2215 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2217     GtkAdjustment *adj = 0;
2219     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2220     gtk_adjustment_set_value(adj, 0.0);
2221     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2222     gtk_adjustment_value_changed(adj);
2224     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2225     gtk_adjustment_set_value(adj, 0.0);
2226     gtk_adjustment_value_changed(adj);
2228     sp_rtb_sensitivize( obj );
2231 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2232                                        gchar const */*old_value*/, gchar const */*new_value*/,
2233                                        bool /*is_interactive*/, gpointer data)
2235     GObject *tbl = G_OBJECT(data);
2237     // quit if run by the _changed callbacks
2238     if (g_object_get_data( tbl, "freeze" )) {
2239         return;
2240     }
2242     // in turn, prevent callbacks from responding
2243     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2245     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2246     SPUnit const *unit = tracker->getActiveUnit();
2248     gpointer item = g_object_get_data( tbl, "item" );
2249     if (item && SP_IS_RECT(item)) {
2250         {
2251             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2252             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2253             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2254         }
2256         {
2257             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2258             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2259             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2260         }
2262         {
2263             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2264             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2265             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2266         }
2268         {
2269             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2270             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2271             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2272         }
2273     }
2275     sp_rtb_sensitivize( tbl );
2277     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2281 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2282     NULL, /* child_added */
2283     NULL, /* child_removed */
2284     rect_tb_event_attr_changed,
2285     NULL, /* content_changed */
2286     NULL  /* order_changed */
2287 };
2289 /**
2290  *  \param selection should not be NULL.
2291  */
2292 static void
2293 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2295     int n_selected = 0;
2296     Inkscape::XML::Node *repr = NULL;
2297     SPItem *item = NULL;
2299     if ( g_object_get_data( tbl, "repr" ) ) {
2300         g_object_set_data( tbl, "item", NULL );
2301     }
2302     purge_repr_listener( tbl, tbl );
2304     for (GSList const *items = selection->itemList();
2305          items != NULL;
2306          items = items->next) {
2307         if (SP_IS_RECT((SPItem *) items->data)) {
2308             n_selected++;
2309             item = (SPItem *) items->data;
2310             repr = SP_OBJECT_REPR(item);
2311         }
2312     }
2314     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2316     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2318     if (n_selected == 0) {
2319         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2321         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2322         gtk_action_set_sensitive(w, FALSE);
2323         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2324         gtk_action_set_sensitive(h, FALSE);
2326     } else if (n_selected == 1) {
2327         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2328         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2330         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2331         gtk_action_set_sensitive(w, TRUE);
2332         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2333         gtk_action_set_sensitive(h, TRUE);
2335         if (repr) {
2336             g_object_set_data( tbl, "repr", repr );
2337             g_object_set_data( tbl, "item", item );
2338             Inkscape::GC::anchor(repr);
2339             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2340             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2341         }
2342     } else {
2343         // FIXME: implement averaging of all parameters for multiple selected
2344         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2345         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2346         sp_rtb_sensitivize( tbl );
2347     }
2351 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2353     EgeAdjustmentAction* eact = 0;
2355     {
2356         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2357         ege_output_action_set_use_markup( act, TRUE );
2358         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2359         g_object_set_data( holder, "mode_action", act );
2360     }
2362     // rx/ry units menu: create
2363     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2364     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2365     // fixme: add % meaning per cent of the width/height
2366     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2367     g_object_set_data( holder, "tracker", tracker );
2369     /* W */
2370     {
2371         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2372         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2373         eact = create_adjustment_action( "RectWidthAction",
2374                                          _("Width"), _("W:"), _("Width of rectangle"),
2375                                          "tools.shapes.rect", "width", 0,
2376                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2377                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2378                                          labels, values, G_N_ELEMENTS(labels),
2379                                          sp_rtb_width_value_changed );
2380         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2381         g_object_set_data( holder, "width_action", eact );
2382         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2383         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2384     }
2386     /* H */
2387     {
2388         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2389         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2390         eact = create_adjustment_action( "RectHeightAction",
2391                                          _("Height"), _("H:"), _("Height of rectangle"),
2392                                          "tools.shapes.rect", "height", 0,
2393                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2394                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2395                                          labels, values, G_N_ELEMENTS(labels),
2396                                          sp_rtb_height_value_changed );
2397         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2398         g_object_set_data( holder, "height_action", eact );
2399         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2400         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2401     }
2403     /* rx */
2404     {
2405         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2406         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2407         eact = create_adjustment_action( "RadiusXAction",
2408                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2409                                          "tools.shapes.rect", "rx", 0,
2410                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2411                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2412                                          labels, values, G_N_ELEMENTS(labels),
2413                                          sp_rtb_rx_value_changed);
2414         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2415         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2416     }
2418     /* ry */
2419     {
2420         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2421         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2422         eact = create_adjustment_action( "RadiusYAction",
2423                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2424                                          "tools.shapes.rect", "ry", 0,
2425                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2426                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2427                                          labels, values, G_N_ELEMENTS(labels),
2428                                          sp_rtb_ry_value_changed);
2429         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2430         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2431     }
2433     // add the units menu
2434     {
2435         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2436         gtk_action_group_add_action( mainActions, act );
2437     }
2439     /* Reset */
2440     {
2441         InkAction* inky = ink_action_new( "RectResetAction",
2442                                           _("Not rounded"),
2443                                           _("Make corners sharp"),
2444                                           "squared_corner",
2445                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2446         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2447         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2448         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2449         g_object_set_data( holder, "not_rounded", inky );
2450     }
2452     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2453     sp_rtb_sensitivize( holder );
2455     sigc::connection *connection = new sigc::connection(
2456         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2457         );
2458     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2459     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2462 //########################
2463 //##       3D Box       ##
2464 //########################
2466 // normalize angle so that it lies in the interval [0,360]
2467 static double box3d_normalize_angle (double a) {
2468     double angle = a + ((int) (a/360.0))*360;
2469     if (angle < 0) {
2470         angle += 360.0;
2471     }
2472     return angle;
2475 static void
2476 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2477                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2478     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2479     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2480     //       are reset).
2481     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2483     if (is_infinite) {
2484         gtk_toggle_action_set_active(tact, TRUE);
2485         gtk_action_set_sensitive(act, TRUE);
2487         double angle = persp3d_get_infinite_angle(persp, axis);
2488         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2489             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2490         }
2491     } else {
2492         gtk_toggle_action_set_active(tact, FALSE);
2493         gtk_action_set_sensitive(act, FALSE);
2494     }
2497 static void
2498 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2499     if (!persp_repr) {
2500         g_print ("No perspective given to box3d_resync_toolbar().\n");
2501         return;
2502     }
2504     GtkWidget *tbl = GTK_WIDGET(data);
2505     GtkAdjustment *adj = 0;
2506     GtkAction *act = 0;
2507     GtkToggleAction *tact = 0;
2508     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2509     {
2510         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2511         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2512         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2514         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2515     }
2516     {
2517         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2518         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2519         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2521         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2522     }
2523     {
2524         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2525         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2526         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2528         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2529     }
2532 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2533                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2534                                                   bool /*is_interactive*/, gpointer data)
2536     GtkWidget *tbl = GTK_WIDGET(data);
2538     // quit if run by the attr_changed listener
2539     // note: it used to work without the differently called freeze_ attributes (here and in
2540     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2541     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2542         return;
2543     }
2545     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2546     // sp_document_maybe_done() when the document is undo insensitive)
2547     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2549     // TODO: Only update the appropriate part of the toolbar
2550 //    if (!strcmp(name, "inkscape:vp_z")) {
2551         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2552 //    }
2554     Persp3D *persp = persp3d_get_from_repr(repr);
2555     persp3d_update_box_reprs(persp);
2557     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2560 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2562     NULL, /* child_added */
2563     NULL, /* child_removed */
2564     box3d_persp_tb_event_attr_changed,
2565     NULL, /* content_changed */
2566     NULL  /* order_changed */
2567 };
2569 /**
2570  *  \param selection Should not be NULL.
2571  */
2572 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2573 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2574 static void
2575 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2577     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2578     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2579     // update the perspectives with infinite VPs and leave the other ones untouched).
2581     Inkscape::XML::Node *persp_repr = NULL;
2582     purge_repr_listener(tbl, tbl);
2584     SPItem *item = selection->singleItem();
2585     if (item && SP_IS_BOX3D(item)) {
2586         // FIXME: Also deal with multiple selected boxes
2587         SPBox3D *box = SP_BOX3D(item);
2588         Persp3D *persp = box3d_get_perspective(box);
2589         persp_repr = SP_OBJECT_REPR(persp);
2590         if (persp_repr) {
2591             g_object_set_data(tbl, "repr", persp_repr);
2592             Inkscape::GC::anchor(persp_repr);
2593             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2594             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2595         }
2597         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2598         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2600         box3d_resync_toolbar(persp_repr, tbl);
2601     }
2604 static void
2605 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2607     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2608     SPDocument *document = sp_desktop_document(desktop);
2610     // quit if run by the attr_changed listener
2611     // note: it used to work without the differently called freeze_ attributes (here and in
2612     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2613     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2614         return;
2615     }
2617     // in turn, prevent listener from responding
2618     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2620     //Persp3D *persp = document->current_persp3d;
2621     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2622     if (sel_persps.empty()) {
2623         // this can happen when the document is created; we silently ignore it
2624         return;
2625     }
2626     Persp3D *persp = sel_persps.front();
2628     persp->tmat.set_infinite_direction (axis, adj->value);
2629     SP_OBJECT(persp)->updateRepr();
2631     // TODO: use the correct axis here, too
2632     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2634     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2638 static void
2639 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2641     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2644 static void
2645 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2647     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2650 static void
2651 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2653     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2657 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2659     // TODO: Take all selected perspectives into account
2660     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2661     if (sel_persps.empty()) {
2662         // this can happen when the document is created; we silently ignore it
2663         return;
2664     }
2665     Persp3D *persp = sel_persps.front();
2667     bool set_infinite = gtk_toggle_action_get_active(act);
2668     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2671 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2673     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2676 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2678     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2681 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2683     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2686 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2688     EgeAdjustmentAction* eact = 0;
2689     SPDocument *document = sp_desktop_document (desktop);
2690     Persp3D *persp = document->current_persp3d;
2692     EgeAdjustmentAction* box3d_angle_x = 0;
2693     EgeAdjustmentAction* box3d_angle_y = 0;
2694     EgeAdjustmentAction* box3d_angle_z = 0;
2696     /* Angle X */
2697     {
2698         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2699         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2700         eact = create_adjustment_action( "3DBoxAngleXAction",
2701                                          _("Angle in X direction"), _("Angle X:"),
2702                                          // Translators: PL is short for 'perspective line'
2703                                          _("Angle of PLs in X direction"),
2704                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2705                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2706                                          -360.0, 360.0, 1.0, 10.0,
2707                                          labels, values, G_N_ELEMENTS(labels),
2708                                          box3d_angle_x_value_changed );
2709         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2710         g_object_set_data( holder, "box3d_angle_x_action", eact );
2711         box3d_angle_x = eact;
2712     }
2714     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2715         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2716     } else {
2717         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2718     }
2721     /* VP X state */
2722     {
2723         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2724                                                       // Translators: VP is short for 'vanishing point'
2725                                                       _("State of VP in X direction"),
2726                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2727                                                       "toggle_vp_x",
2728                                                       Inkscape::ICON_SIZE_DECORATION );
2729         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2730         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2731         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2732         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2733         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2734     }
2736     /* Angle Y */
2737     {
2738         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2739         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2740         eact = create_adjustment_action( "3DBoxAngleYAction",
2741                                          _("Angle in Y direction"), _("Angle Y:"),
2742                                          // Translators: PL is short for 'perspective line'
2743                                          _("Angle of PLs in Y direction"),
2744                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2745                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2746                                          -360.0, 360.0, 1.0, 10.0,
2747                                          labels, values, G_N_ELEMENTS(labels),
2748                                          box3d_angle_y_value_changed );
2749         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2750         g_object_set_data( holder, "box3d_angle_y_action", eact );
2751         box3d_angle_y = eact;
2752     }
2754     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2755         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2756     } else {
2757         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2758     }
2760     /* VP Y state */
2761     {
2762         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2763                                                       // Translators: VP is short for 'vanishing point'
2764                                                       _("State of VP in Y direction"),
2765                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2766                                                       "toggle_vp_y",
2767                                                       Inkscape::ICON_SIZE_DECORATION );
2768         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2769         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2770         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2771         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2772         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2773     }
2775     /* Angle Z */
2776     {
2777         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2778         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2779         eact = create_adjustment_action( "3DBoxAngleZAction",
2780                                          _("Angle in Z direction"), _("Angle Z:"),
2781                                          // Translators: PL is short for 'perspective line'
2782                                          _("Angle of PLs in Z direction"),
2783                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2784                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2785                                          -360.0, 360.0, 1.0, 10.0,
2786                                          labels, values, G_N_ELEMENTS(labels),
2787                                          box3d_angle_z_value_changed );
2788         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2789         g_object_set_data( holder, "box3d_angle_z_action", eact );
2790         box3d_angle_z = eact;
2791     }
2793     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2794         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2795     } else {
2796         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2797     }
2799     /* VP Z state */
2800     {
2801         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2802                                                       // Translators: VP is short for 'vanishing point'
2803                                                       _("State of VP in Z direction"),
2804                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2805                                                       "toggle_vp_z",
2806                                                       Inkscape::ICON_SIZE_DECORATION );
2807         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2808         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2809         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2810         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2811         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2812     }
2814     sigc::connection *connection = new sigc::connection(
2815         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2816        );
2817     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2818     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2821 //########################
2822 //##       Spiral       ##
2823 //########################
2825 static void
2826 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2828     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2830     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2831         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2832     }
2834     // quit if run by the attr_changed listener
2835     if (g_object_get_data( tbl, "freeze" )) {
2836         return;
2837     }
2839     // in turn, prevent listener from responding
2840     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2842     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2844     bool modmade = false;
2845     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2846          items != NULL;
2847          items = items->next)
2848     {
2849         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2850             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2851             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2852             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2853             modmade = true;
2854         }
2855     }
2857     g_free(namespaced_name);
2859     if (modmade) {
2860         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2861                                    _("Change spiral"));
2862     }
2864     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2867 static void
2868 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2870     sp_spl_tb_value_changed(adj, tbl, "revolution");
2873 static void
2874 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2876     sp_spl_tb_value_changed(adj, tbl, "expansion");
2879 static void
2880 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2882     sp_spl_tb_value_changed(adj, tbl, "t0");
2885 static void
2886 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2888     GtkWidget *tbl = GTK_WIDGET(obj);
2890     GtkAdjustment *adj;
2892     // fixme: make settable
2893     gdouble rev = 5;
2894     gdouble exp = 1.0;
2895     gdouble t0 = 0.0;
2897     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2898     gtk_adjustment_set_value(adj, rev);
2899     gtk_adjustment_value_changed(adj);
2901     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2902     gtk_adjustment_set_value(adj, exp);
2903     gtk_adjustment_value_changed(adj);
2905     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2906     gtk_adjustment_set_value(adj, t0);
2907     gtk_adjustment_value_changed(adj);
2909     spinbutton_defocus(GTK_OBJECT(tbl));
2913 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2914                                          gchar const */*old_value*/, gchar const */*new_value*/,
2915                                          bool /*is_interactive*/, gpointer data)
2917     GtkWidget *tbl = GTK_WIDGET(data);
2919     // quit if run by the _changed callbacks
2920     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2921         return;
2922     }
2924     // in turn, prevent callbacks from responding
2925     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2927     GtkAdjustment *adj;
2928     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2929     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2931     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2932     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2934     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2935     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2937     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2941 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2942     NULL, /* child_added */
2943     NULL, /* child_removed */
2944     spiral_tb_event_attr_changed,
2945     NULL, /* content_changed */
2946     NULL  /* order_changed */
2947 };
2949 static void
2950 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2952     int n_selected = 0;
2953     Inkscape::XML::Node *repr = NULL;
2955     purge_repr_listener( tbl, tbl );
2957     for (GSList const *items = selection->itemList();
2958          items != NULL;
2959          items = items->next)
2960     {
2961         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2962             n_selected++;
2963             repr = SP_OBJECT_REPR((SPItem *) items->data);
2964         }
2965     }
2967     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2969     if (n_selected == 0) {
2970         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2971     } else if (n_selected == 1) {
2972         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2974         if (repr) {
2975             g_object_set_data( tbl, "repr", repr );
2976             Inkscape::GC::anchor(repr);
2977             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2978             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2979         }
2980     } else {
2981         // FIXME: implement averaging of all parameters for multiple selected
2982         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2983         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2984     }
2988 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2990     EgeAdjustmentAction* eact = 0;
2992     {
2993         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2994         ege_output_action_set_use_markup( act, TRUE );
2995         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2996         g_object_set_data( holder, "mode_action", act );
2997     }
2999     /* Revolution */
3000     {
3001         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3002         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3003         eact = create_adjustment_action( "SpiralRevolutionAction",
3004                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3005                                          "tools.shapes.spiral", "revolution", 3.0,
3006                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3007                                          0.01, 1024.0, 0.1, 1.0,
3008                                          labels, values, G_N_ELEMENTS(labels),
3009                                          sp_spl_tb_revolution_value_changed, 1, 2);
3010         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3011     }
3013     /* Expansion */
3014     {
3015         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3016         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3017         eact = create_adjustment_action( "SpiralExpansionAction",
3018                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3019                                          "tools.shapes.spiral", "expansion", 1.0,
3020                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3021                                          0.0, 1000.0, 0.01, 1.0,
3022                                          labels, values, G_N_ELEMENTS(labels),
3023                                          sp_spl_tb_expansion_value_changed);
3024         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3025     }
3027     /* T0 */
3028     {
3029         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3030         gdouble values[] = {0, 0.5, 0.9};
3031         eact = create_adjustment_action( "SpiralT0Action",
3032                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3033                                          "tools.shapes.spiral", "t0", 0.0,
3034                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3035                                          0.0, 0.999, 0.01, 1.0,
3036                                          labels, values, G_N_ELEMENTS(labels),
3037                                          sp_spl_tb_t0_value_changed);
3038         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3039     }
3041     /* Reset */
3042     {
3043         InkAction* inky = ink_action_new( "SpiralResetAction",
3044                                           _("Defaults"),
3045                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3046                                           GTK_STOCK_CLEAR,
3047                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3048         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3049         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3050     }
3053     sigc::connection *connection = new sigc::connection(
3054         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3055         );
3056     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3057     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3060 //########################
3061 //##     Pen/Pencil    ##
3062 //########################
3065 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3067     // Put stuff here
3070 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3072     // Put stuff here
3075 //########################
3076 //##       Tweak        ##
3077 //########################
3079 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3081     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3084 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3086     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3089 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3091     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3094 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3096     int mode = ege_select_one_action_get_active( act );
3097     prefs_set_int_attribute("tools.tweak", "mode", mode);
3099     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3100     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3101     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3102     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3103     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3104     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3105     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3106         if (doh) gtk_action_set_sensitive (doh, TRUE);
3107         if (dos) gtk_action_set_sensitive (dos, TRUE);
3108         if (dol) gtk_action_set_sensitive (dol, TRUE);
3109         if (doo) gtk_action_set_sensitive (doo, TRUE);
3110         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3111         if (fid) gtk_action_set_sensitive (fid, FALSE);
3112     } else {
3113         if (doh) gtk_action_set_sensitive (doh, FALSE);
3114         if (dos) gtk_action_set_sensitive (dos, FALSE);
3115         if (dol) gtk_action_set_sensitive (dol, FALSE);
3116         if (doo) gtk_action_set_sensitive (doo, FALSE);
3117         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3118         if (fid) gtk_action_set_sensitive (fid, TRUE);
3119     }
3122 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3124     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3127 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3128     bool show = gtk_toggle_action_get_active( act );
3129     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3131 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3132     bool show = gtk_toggle_action_get_active( act );
3133     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3135 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3136     bool show = gtk_toggle_action_get_active( act );
3137     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3139 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3140     bool show = gtk_toggle_action_get_active( act );
3141     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3144 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3146     {
3147         /* Width */
3148         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3149         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3150         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3151                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3152                                                               "tools.tweak", "width", 15,
3153                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3154                                                               1, 100, 1.0, 10.0,
3155                                                               labels, values, G_N_ELEMENTS(labels),
3156                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3157         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3158         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3159     }
3162     {
3163         /* Force */
3164         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3165         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3166         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3167                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3168                                                               "tools.tweak", "force", 20,
3169                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3170                                                               1, 100, 1.0, 10.0,
3171                                                               labels, values, G_N_ELEMENTS(labels),
3172                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3173         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3174         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3175     }
3177     /* Mode */
3178     {
3179         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3181         GtkTreeIter iter;
3182         gtk_list_store_append( model, &iter );
3183         gtk_list_store_set( model, &iter,
3184                             0, _("Push mode"),
3185                             1, _("Push parts of paths in any direction"),
3186                             2, "tweak_push_mode",
3187                             -1 );
3189         gtk_list_store_append( model, &iter );
3190         gtk_list_store_set( model, &iter,
3191                             0, _("Shrink mode"),
3192                             1, _("Shrink (inset) parts of paths"),
3193                             2, "tweak_shrink_mode",
3194                             -1 );
3196         gtk_list_store_append( model, &iter );
3197         gtk_list_store_set( model, &iter,
3198                             0, _("Grow mode"),
3199                             1, _("Grow (outset) parts of paths"),
3200                             2, "tweak_grow_mode",
3201                             -1 );
3203         gtk_list_store_append( model, &iter );
3204         gtk_list_store_set( model, &iter,
3205                             0, _("Attract mode"),
3206                             1, _("Attract parts of paths towards cursor"),
3207                             2, "tweak_attract_mode",
3208                             -1 );
3210         gtk_list_store_append( model, &iter );
3211         gtk_list_store_set( model, &iter,
3212                             0, _("Repel mode"),
3213                             1, _("Repel parts of paths from cursor"),
3214                             2, "tweak_repel_mode",
3215                             -1 );
3217         gtk_list_store_append( model, &iter );
3218         gtk_list_store_set( model, &iter,
3219                             0, _("Roughen mode"),
3220                             1, _("Roughen parts of paths"),
3221                             2, "tweak_roughen_mode",
3222                             -1 );
3224         gtk_list_store_append( model, &iter );
3225         gtk_list_store_set( model, &iter,
3226                             0, _("Color paint mode"),
3227                             1, _("Paint the tool's color upon selected objects"),
3228                             2, "tweak_colorpaint_mode",
3229                             -1 );
3231         gtk_list_store_append( model, &iter );
3232         gtk_list_store_set( model, &iter,
3233                             0, _("Color jitter mode"),
3234                             1, _("Jitter the colors of selected objects"),
3235                             2, "tweak_colorjitter_mode",
3236                             -1 );
3238         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3239         g_object_set( act, "short_label", _("Mode:"), NULL );
3240         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3241         g_object_set_data( holder, "mode_action", act );
3243         ege_select_one_action_set_appearance( act, "full" );
3244         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3245         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3246         ege_select_one_action_set_icon_column( act, 2 );
3247         ege_select_one_action_set_tooltip_column( act, 1  );
3249         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3250         ege_select_one_action_set_active( act, mode );
3251         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3253         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3254     }
3256     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3258     {
3259         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3260         ege_output_action_set_use_markup( act, TRUE );
3261         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3262         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3263             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3264         g_object_set_data( holder, "tweak_channels_label", act);
3265     }
3267     {
3268         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3269                                                       _("Hue"),
3270                                                       _("In color mode, act on objects' hue"),
3271                                                       NULL,
3272                                                       Inkscape::ICON_SIZE_DECORATION );
3273         g_object_set( act, "short_label", _("H"), NULL );
3274         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3275         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3276         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3277         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3278             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3279         g_object_set_data( holder, "tweak_doh", act);
3280     }
3281     {
3282         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3283                                                       _("Saturation"),
3284                                                       _("In color mode, act on objects' saturation"),
3285                                                       NULL,
3286                                                       Inkscape::ICON_SIZE_DECORATION );
3287         g_object_set( act, "short_label", _("S"), NULL );
3288         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3289         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3290         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3291         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3292             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3293         g_object_set_data( holder, "tweak_dos", act );
3294     }
3295     {
3296         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3297                                                       _("Lightness"),
3298                                                       _("In color mode, act on objects' lightness"),
3299                                                       NULL,
3300                                                       Inkscape::ICON_SIZE_DECORATION );
3301         g_object_set( act, "short_label", _("L"), NULL );
3302         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3303         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3304         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3305         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3306             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3307         g_object_set_data( holder, "tweak_dol", act );
3308     }
3309     {
3310         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3311                                                       _("Opacity"),
3312                                                       _("In color mode, act on objects' opacity"),
3313                                                       NULL,
3314                                                       Inkscape::ICON_SIZE_DECORATION );
3315         g_object_set( act, "short_label", _("O"), NULL );
3316         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3317         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3318         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3319         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3320             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3321         g_object_set_data( holder, "tweak_doo", act );
3322     }
3324     {   /* Fidelity */
3325         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3326         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3327         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3328                                                               _("Fidelity"), _("Fidelity:"),
3329                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3330                                                               "tools.tweak", "fidelity", 50,
3331                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3332                                                               1, 100, 1.0, 10.0,
3333                                                               labels, values, G_N_ELEMENTS(labels),
3334                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3335         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3336         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3337         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3338             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3339         g_object_set_data( holder, "tweak_fidelity", eact );
3340     }
3343     /* Use Pressure button */
3344     {
3345         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3346                                                       _("Pressure"),
3347                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3348                                                       "use_pressure",
3349                                                       Inkscape::ICON_SIZE_DECORATION );
3350         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3351         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3352         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3353     }
3358 //########################
3359 //##     Calligraphy    ##
3360 //########################
3361 static void update_presets_list(GObject *dataKludge ){
3362     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3363     if (sel) {
3364         ege_select_one_action_set_active(sel, 0 );
3365     }
3368 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3370     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3371     update_presets_list(tbl);
3374 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3376     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3377     update_presets_list(tbl);
3380 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3382     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3383     update_presets_list(tbl);
3386 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3388     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3389     update_presets_list(tbl);
3392 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3394     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3395     update_presets_list(tbl);
3398 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3400     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3401     update_presets_list(tbl);
3404 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3406     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3407     update_presets_list(tbl);
3410 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3412     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3413     update_presets_list(tbl);
3416 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3418     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3419     update_presets_list(tbl);
3422 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3424     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3425     update_presets_list(tbl);
3428 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3430     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3431     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3432     update_presets_list(tbl);
3433     if (calligraphy_angle )
3434         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3438 #define PROFILE_FLOAT_SIZE 7
3439 #define PROFILE_INT_SIZE 4
3440 struct ProfileFloatElement {
3441     char const *name;
3442     double def;
3443     double min;
3444     double max;
3445 };
3446 struct ProfileIntElement {
3447     char const *name;
3448     int def;
3449     int min;
3450     int max;
3451 };
3455 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3456     {"mass",0.02, 0.0, 1.0},
3457     {"wiggle",0.0, 0.0, 1.0},
3458     {"angle",30.0, -90.0, 90.0},
3459     {"thinning",0.1, -1.0, 1.0},
3460     {"tremor",0.0, 0.0, 1.0},
3461     {"flatness",0.9, 0.0, 1.0},
3462     {"cap_rounding",0.0, 0.0, 5.0}
3463 };
3464 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3465     {"width",15, 1, 100},
3466     {"usepressure",1,0,1},
3467     {"tracebackground",0,0,1},
3468     {"usetilt",1,0,1},
3469 };
3473 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3474     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3475     if (! desktop) return;
3477     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3478     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3479     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3481     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3482     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3483     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3484     
3485     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3486         ProfileFloatElement const &pe = f_profile[i];
3487         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3488         prefs_set_double_attribute(pref_path,pe.name,v);
3489     }    
3490     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3491         ProfileIntElement const &pe = i_profile[i];
3492         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3493         prefs_set_int_attribute(pref_path,pe.name,v);
3494     }
3495     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3497     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3498     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3499     GtkTreeIter iter;
3500     gtk_list_store_append( model, &iter );
3501     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3503     free(profile_id);
3504     free(pref_path);
3506     ege_select_one_action_set_active(selector, new_index);
3510 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3511     
3512     gint preset_index = ege_select_one_action_get_active( act ); 
3513     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3514        
3515     if ( profile_name) {        
3516         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3517         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3518             ProfileFloatElement const &pe = f_profile[i];
3519             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3520             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3521             if ( adj ) {
3522                 gtk_adjustment_set_value(adj, v);
3523             }
3524         } 
3525         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3526             ProfileIntElement const &pe = i_profile[i];
3527             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3528             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3529             if ( toggle ) {
3530                 gtk_toggle_action_set_active(toggle, v);
3531             } else printf("No toggle");
3532         }
3533         free(profile_name);
3534         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3535     }
3540 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3542     {
3543         EgeAdjustmentAction* calligraphy_angle = 0;
3545         {
3546         /* Width */
3547         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3548         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3549         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3550                                                               _("Pen Width"), _("Width:"),
3551                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3552                                                               "tools.calligraphic", "width", 15,
3553                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3554                                                               1, 100, 1.0, 10.0,
3555                                                               labels, values, G_N_ELEMENTS(labels),
3556                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3557         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3558         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3559         }
3561         {
3562         /* Thinning */
3563             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3564             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3565         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3566                                                               _("Stroke Thinning"), _("Thinning:"),
3567                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3568                                                               "tools.calligraphic", "thinning", 0.1,
3569                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3570                                                               -1.0, 1.0, 0.01, 0.1,
3571                                                               labels, values, G_N_ELEMENTS(labels),
3572                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3573         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3574         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3575         }
3577         {
3578         /* Angle */
3579         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3580         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3581         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3582                                                               _("Pen Angle"), _("Angle:"),
3583                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3584                                                               "tools.calligraphic", "angle", 30,
3585                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3586                                                               -90.0, 90.0, 1.0, 10.0,
3587                                                               labels, values, G_N_ELEMENTS(labels),
3588                                                               sp_ddc_angle_value_changed, 1, 0 );
3589         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3590         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3591         g_object_set_data( holder, "angle", eact );
3592         calligraphy_angle = eact;
3593         }
3595         {
3596         /* Fixation */
3597             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3598         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3599         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3600                                                               _("Fixation"), _("Fixation:"),
3601                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3602                                                               "tools.calligraphic", "flatness", 0.9,
3603                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3604                                                               0.0, 1.0, 0.01, 0.1,
3605                                                               labels, values, G_N_ELEMENTS(labels),
3606                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3607         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3608         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3609         }
3611         {
3612         /* Cap Rounding */
3613             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3614         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3615         // TRANSLATORS: "cap" means "end" (both start and finish) here
3616         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3617                                                               _("Cap rounding"), _("Caps:"),
3618                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3619                                                               "tools.calligraphic", "cap_rounding", 0.0,
3620                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3621                                                               0.0, 5.0, 0.01, 0.1,
3622                                                               labels, values, G_N_ELEMENTS(labels),
3623                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3624         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3625         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3626         }
3628         {
3629         /* Tremor */
3630             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3631         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3632         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3633                                                               _("Stroke Tremor"), _("Tremor:"),
3634                                                               _("Increase to make strokes rugged and trembling"),
3635                                                               "tools.calligraphic", "tremor", 0.0,
3636                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3637                                                               0.0, 1.0, 0.01, 0.1,
3638                                                               labels, values, G_N_ELEMENTS(labels),
3639                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3641         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3642         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3643         }
3645         {
3646         /* Wiggle */
3647         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3648         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3649         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3650                                                               _("Pen Wiggle"), _("Wiggle:"),
3651                                                               _("Increase to make the pen waver and wiggle"),
3652                                                               "tools.calligraphic", "wiggle", 0.0,
3653                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3654                                                               0.0, 1.0, 0.01, 0.1,
3655                                                               labels, values, G_N_ELEMENTS(labels),
3656                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3657         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3658         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3659         }
3661         {
3662         /* Mass */
3663             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3664         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3665         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3666                                                               _("Pen Mass"), _("Mass:"),
3667                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3668                                                               "tools.calligraphic", "mass", 0.02,
3669                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3670                                                               0.0, 1.0, 0.01, 0.1,
3671                                                               labels, values, G_N_ELEMENTS(labels),
3672                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3673         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3674         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3675         }
3678         /* Trace Background button */
3679         {
3680             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3681                                                           _("Trace Background"),
3682                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3683                                                           "trace_background",
3684                                                           Inkscape::ICON_SIZE_DECORATION );
3685             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3686             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3687             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3688             g_object_set_data( holder, "tracebackground", act );
3689         }
3691         /* Use Pressure button */
3692         {
3693             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3694                                                           _("Pressure"),
3695                                                           _("Use the pressure of the input device to alter the width of the pen"),
3696                                                           "use_pressure",
3697                                                           Inkscape::ICON_SIZE_DECORATION );
3698             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3699             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3700             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3701             g_object_set_data( holder, "usepressure", act );
3702         }
3704         /* Use Tilt button */
3705         {
3706             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3707                                                           _("Tilt"),
3708                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3709                                                           "use_tilt",
3710                                                           Inkscape::ICON_SIZE_DECORATION );
3711             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3712             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3713             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3714             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3715             g_object_set_data( holder, "usetilt", act );
3716         }
3718         /*calligraphic profile */
3719         {
3720             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3721             gchar *pref_path;
3724             GtkTreeIter iter;
3725             gtk_list_store_append( model, &iter );
3726             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3728             //TODO: switch back to prefs API
3729             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3730             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3731             int ii=1;
3732             while (child_repr) {
3733                 GtkTreeIter iter;
3734                 char *preset_name = (char *) child_repr->attribute("name");
3735                 gtk_list_store_append( model, &iter );                
3736                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
3737                 child_repr = sp_repr_next(child_repr); 
3738             }
3740             pref_path = NULL;
3741             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3742             ege_select_one_action_set_appearance( act1, "compact" );
3743             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3744             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3745             g_object_set_data( holder, "profile_selector", act1 );
3746         
3747         }
3749         /*Save or delete calligraphic profile */
3750         {
3751             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3752                                              _("Defaults"),
3753                                              _("Save current settings as new profile"),
3754                                              GTK_STOCK_SAVE );
3755             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3758             gtk_action_group_add_action( mainActions, act );
3759             gtk_action_set_sensitive( act, TRUE );
3760             g_object_set_data( holder, "profile_save_delete", act );
3761         }
3762     }    
3766 //########################
3767 //##    Circle / Arc    ##
3768 //########################
3770 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3772     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3773     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3775     if (v1 == 0 && v2 == 0) {
3776         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3777             gtk_action_set_sensitive( ocb, FALSE );
3778             gtk_action_set_sensitive( make_whole, FALSE );
3779         }
3780     } else {
3781         gtk_action_set_sensitive( ocb, TRUE );
3782         gtk_action_set_sensitive( make_whole, TRUE );
3783     }
3786 static void
3787 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3789     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3791     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3792         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3793     }
3795     // quit if run by the attr_changed listener
3796     if (g_object_get_data( tbl, "freeze" )) {
3797         return;
3798     }
3800     // in turn, prevent listener from responding
3801     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3803     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3805     bool modmade = false;
3806     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3807          items != NULL;
3808          items = items->next)
3809     {
3810         SPItem *item = SP_ITEM(items->data);
3812         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3814             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3815             SPArc *arc = SP_ARC(item);
3817             if (!strcmp(value_name, "start"))
3818                 ge->start = (adj->value * M_PI)/ 180;
3819             else
3820                 ge->end = (adj->value * M_PI)/ 180;
3822             sp_genericellipse_normalize(ge);
3823             ((SPObject *)arc)->updateRepr();
3824             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3826             modmade = true;
3827         }
3828     }
3830     g_free(namespaced_name);
3832     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3834     sp_arctb_sensitivize( tbl, adj->value, other->value );
3836     if (modmade) {
3837         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3838                                    _("Arc: Change start/end"));
3839     }
3841     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3845 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3847     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3850 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3852     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3855 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3857     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3858     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3859         if ( ege_select_one_action_get_active( act ) != 0 ) {
3860             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3861         } else {
3862             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3863         }
3864     }
3866     // quit if run by the attr_changed listener
3867     if (g_object_get_data( tbl, "freeze" )) {
3868         return;
3869     }
3871     // in turn, prevent listener from responding
3872     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3874     bool modmade = false;
3876     if ( ege_select_one_action_get_active(act) != 0 ) {
3877         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3878              items != NULL;
3879              items = items->next)
3880         {
3881             if (SP_IS_ARC((SPItem *) items->data)) {
3882                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3883                 repr->setAttribute("sodipodi:open", "true");
3884                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3885                 modmade = true;
3886             }
3887         }
3888     } else {
3889         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3890              items != NULL;
3891              items = items->next)
3892         {
3893             if (SP_IS_ARC((SPItem *) items->data))    {
3894                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3895                 repr->setAttribute("sodipodi:open", NULL);
3896                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3897                 modmade = true;
3898             }
3899         }
3900     }
3902     if (modmade) {
3903         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3904                                    _("Arc: Change open/closed"));
3905     }
3907     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3910 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3912     GtkAdjustment *adj;
3913     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3914     gtk_adjustment_set_value(adj, 0.0);
3915     gtk_adjustment_value_changed(adj);
3917     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3918     gtk_adjustment_set_value(adj, 0.0);
3919     gtk_adjustment_value_changed(adj);
3921     spinbutton_defocus( GTK_OBJECT(obj) );
3924 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3925                                       gchar const */*old_value*/, gchar const */*new_value*/,
3926                                       bool /*is_interactive*/, gpointer data)
3928     GObject *tbl = G_OBJECT(data);
3930     // quit if run by the _changed callbacks
3931     if (g_object_get_data( tbl, "freeze" )) {
3932         return;
3933     }
3935     // in turn, prevent callbacks from responding
3936     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3938     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3939     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3941     GtkAdjustment *adj1,*adj2;
3942     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3943     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3944     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3945     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3947     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3949     char const *openstr = NULL;
3950     openstr = repr->attribute("sodipodi:open");
3951     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3953     if (openstr) {
3954         ege_select_one_action_set_active( ocb, 1 );
3955     } else {
3956         ege_select_one_action_set_active( ocb, 0 );
3957     }
3959     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3962 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3963     NULL, /* child_added */
3964     NULL, /* child_removed */
3965     arc_tb_event_attr_changed,
3966     NULL, /* content_changed */
3967     NULL  /* order_changed */
3968 };
3971 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3973     int n_selected = 0;
3974     Inkscape::XML::Node *repr = NULL;
3976     purge_repr_listener( tbl, tbl );
3978     for (GSList const *items = selection->itemList();
3979          items != NULL;
3980          items = items->next)
3981     {
3982         if (SP_IS_ARC((SPItem *) items->data)) {
3983             n_selected++;
3984             repr = SP_OBJECT_REPR((SPItem *) items->data);
3985         }
3986     }
3988     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3990     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3991     if (n_selected == 0) {
3992         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3993     } else if (n_selected == 1) {
3994         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3995         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3997         if (repr) {
3998             g_object_set_data( tbl, "repr", repr );
3999             Inkscape::GC::anchor(repr);
4000             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4001             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4002         }
4003     } else {
4004         // FIXME: implement averaging of all parameters for multiple selected
4005         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4006         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4007         sp_arctb_sensitivize( tbl, 1, 0 );
4008     }
4012 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4014     EgeAdjustmentAction* eact = 0;
4017     {
4018         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4019         ege_output_action_set_use_markup( act, TRUE );
4020         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4021         g_object_set_data( holder, "mode_action", act );
4022     }
4024     /* Start */
4025     {
4026         eact = create_adjustment_action( "ArcStartAction",
4027                                          _("Start"), _("Start:"),
4028                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4029                                          "tools.shapes.arc", "start", 0.0,
4030                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4031                                          -360.0, 360.0, 1.0, 10.0,
4032                                          0, 0, 0,
4033                                          sp_arctb_start_value_changed);
4034         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4035     }
4037     /* End */
4038     {
4039         eact = create_adjustment_action( "ArcEndAction",
4040                                          _("End"), _("End:"),
4041                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4042                                          "tools.shapes.arc", "end", 0.0,
4043                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4044                                          -360.0, 360.0, 1.0, 10.0,
4045                                          0, 0, 0,
4046                                          sp_arctb_end_value_changed);
4047         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4048     }
4050     /* Segments / Pie checkbox */
4051     {
4052         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4054         GtkTreeIter iter;
4055         gtk_list_store_append( model, &iter );
4056         gtk_list_store_set( model, &iter,
4057                             0, _("Closed arc"),
4058                             1, _("Switch to segment (closed shape with two radii)"),
4059                             2, "circle_closed_arc",
4060                             -1 );
4062         gtk_list_store_append( model, &iter );
4063         gtk_list_store_set( model, &iter,
4064                             0, _("Open Arc"),
4065                             1, _("Switch to arc (unclosed shape)"),
4066                             2, "circle_open_arc",
4067                             -1 );
4069         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4070         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4071         g_object_set_data( holder, "open_action", act );
4073         ege_select_one_action_set_appearance( act, "full" );
4074         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4075         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4076         ege_select_one_action_set_icon_column( act, 2 );
4077         ege_select_one_action_set_tooltip_column( act, 1  );
4079         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4080         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4081         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4082         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4083     }
4085     /* Make Whole */
4086     {
4087         InkAction* inky = ink_action_new( "ArcResetAction",
4088                                           _("Make whole"),
4089                                           _("Make the shape a whole ellipse, not arc or segment"),
4090                                           "reset_circle",
4091                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4092         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4093         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4094         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4095         g_object_set_data( holder, "make_whole", inky );
4096     }
4098     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4099     // sensitivize make whole and open checkbox
4100     {
4101         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4102         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4103         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4104     }
4107     sigc::connection *connection = new sigc::connection(
4108         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4109         );
4110     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4111     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4117 // toggle button callbacks and updaters
4119 //########################
4120 //##      Dropper       ##
4121 //########################
4123 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4124     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4125     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4126     if ( set_action ) {
4127         if ( gtk_toggle_action_get_active( act ) ) {
4128             gtk_action_set_sensitive( set_action, TRUE );
4129         } else {
4130             gtk_action_set_sensitive( set_action, FALSE );
4131         }
4132     }
4134     spinbutton_defocus(GTK_OBJECT(tbl));
4137 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4138     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4139     spinbutton_defocus(GTK_OBJECT(tbl));
4143 /**
4144  * Dropper auxiliary toolbar construction and setup.
4145  *
4146  * TODO: Would like to add swatch of current color.
4147  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4148  *       can drag and drop places. Will provide a nice mixing palette.
4149  */
4150 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4152     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4154     {
4155         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4156         ege_output_action_set_use_markup( act, TRUE );
4157         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4158     }
4160     {
4161         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4162                                                       _("Pick opacity"),
4163                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4164                                                       NULL,
4165                                                       Inkscape::ICON_SIZE_DECORATION );
4166         g_object_set( act, "short_label", _("Pick"), NULL );
4167         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4168         g_object_set_data( holder, "pick_action", act );
4169         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4170         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4171     }
4173     {
4174         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4175                                                       _("Assign opacity"),
4176                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4177                                                       NULL,
4178                                                       Inkscape::ICON_SIZE_DECORATION );
4179         g_object_set( act, "short_label", _("Assign"), NULL );
4180         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4181         g_object_set_data( holder, "set_action", act );
4182         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4183         // make sure it's disabled if we're not picking alpha
4184         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4185         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4186     }
4190 //########################
4191 //##    Text Toolbox    ##
4192 //########################
4193 /*
4194 static void
4195 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4197     //Call back for letter sizing spinbutton
4200 static void
4201 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4203     //Call back for line height spinbutton
4206 static void
4207 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4209     //Call back for horizontal kerning spinbutton
4212 static void
4213 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4215     //Call back for vertical kerning spinbutton
4218 static void
4219 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4221     //Call back for letter rotation spinbutton
4222 }*/
4224 namespace {
4226 bool popdown_visible = false;
4227 bool popdown_hasfocus = false;
4229 void
4230 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4232     SPStyle *query =
4233         sp_style_new (SP_ACTIVE_DOCUMENT);
4235     int result_fontspec =
4236         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4238     int result_family =
4239         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4241     int result_style =
4242         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4244     int result_numbers =
4245         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4247     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4249     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4250     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4251     {
4252         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4254         if (repr)
4255         {
4256             sp_style_read_from_repr (query, repr);
4257         }
4258         else
4259         {
4260             return;
4261         }
4262     }
4264     if (query->text)
4265     {
4266         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4267             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4268             gtk_entry_set_text (GTK_ENTRY (entry), "");
4270         } else if (query->text->font_specification.value || query->text->font_family.value) {
4272             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4274             // Get the font that corresponds
4275             Glib::ustring familyName;
4277             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4278             if (font) {
4279                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4280                 font->Unref();
4281                 font = NULL;
4282             }
4284             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4286             Gtk::TreePath path;
4287             try {
4288                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4289             } catch (...) {
4290                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4291                 return;
4292             }
4294             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4295             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4297             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4299             gtk_tree_selection_select_path (tselection, path.gobj());
4300             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4302             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4303         }
4305         //Size
4306         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4307         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4308         g_object_set_data (tbl, "size-block", gpointer(1));
4309         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4310         g_object_set_data (tbl, "size-block", gpointer(0));
4311         free (str);
4313         //Anchor
4314         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4315         {
4316             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4317             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4318             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4319             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4320         }
4321         else
4322         {
4323             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4324             {
4325                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4326                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4327                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4328                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4329             }
4330             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4331             {
4332                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4333                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4334                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4335                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4336             }
4337             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4338             {
4339                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4340                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4341                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4342                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4343             }
4344         }
4346         //Style
4347         {
4348             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4350             gboolean active = gtk_toggle_button_get_active (button);
4351             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4353             if (active != check)
4354             {
4355                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4356                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4357                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4358             }
4359         }
4361         {
4362             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4364             gboolean active = gtk_toggle_button_get_active (button);
4365             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4367             if (active != check)
4368             {
4369                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4370                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4371                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4372             }
4373         }
4375         //Orientation
4376         //locking both buttons, changing one affect all group (both)
4377         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4378         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4380         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4381         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4383         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4384         {
4385             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4386         }
4387         else
4388         {
4389             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4390         }
4391         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4392         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4393     }
4395     sp_style_unref(query);
4398 void
4399 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4401     sp_text_toolbox_selection_changed (selection, tbl);
4404 void
4405 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4407     sp_text_toolbox_selection_changed (NULL, tbl);
4410 void
4411 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4412                                 GObject             *tbl)
4414     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4415     GtkTreeModel *model = 0;
4416     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4417     GtkTreeIter   iter;
4418     char         *family = 0;
4420     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4421     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4423     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4424         return;
4425     }
4427     gtk_tree_model_get (model, &iter, 0, &family, -1);
4429     if (g_object_get_data (G_OBJECT (selection), "block"))
4430     {
4431         gtk_entry_set_text (GTK_ENTRY (entry), family);
4432         return;
4433     }
4435     gtk_entry_set_text (GTK_ENTRY (entry), family);
4437     SPStyle *query =
4438         sp_style_new (SP_ACTIVE_DOCUMENT);
4440     int result_fontspec =
4441         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4443     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4445     SPCSSAttr *css = sp_repr_css_attr_new ();
4448     // First try to get the font spec from the stored value
4449     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4451     if (fontSpec.empty()) {
4452         // Construct a new font specification if it does not yet exist
4453         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4454         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4455         fontFromStyle->Unref();
4456     }
4458     if (!fontSpec.empty()) {
4459         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4460         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4461             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4462             if (font) {
4463                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4465                 // Set all the these just in case they were altered when finding the best
4466                 // match for the new family and old style...
4468                 gchar c[256];
4470                 font->Family(c, 256);
4471                 sp_repr_css_set_property (css, "font-family", c);
4473                 font->Attribute( "weight", c, 256);
4474                 sp_repr_css_set_property (css, "font-weight", c);
4476                 font->Attribute("style", c, 256);
4477                 sp_repr_css_set_property (css, "font-style", c);
4479                 font->Attribute("stretch", c, 256);
4480                 sp_repr_css_set_property (css, "font-stretch", c);
4482                 font->Attribute("variant", c, 256);
4483                 sp_repr_css_set_property (css, "font-variant", c);
4485                 font->Unref();
4486             }
4487         }
4488     }
4490     // If querying returned nothing, set the default style of the tool (for new texts)
4491     if (result_fontspec == QUERY_STYLE_NOTHING)
4492     {
4493         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4494         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4495     }
4496     else
4497     {
4498         sp_desktop_set_style (desktop, css, true, true);
4499     }
4501     sp_style_unref(query);
4503     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4504                                    _("Text: Change font family"));
4505     sp_repr_css_attr_unref (css);
4506     free (family);
4507     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4509     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4512 void
4513 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4514                                        GObject      *tbl)
4516     const char *family = gtk_entry_get_text (entry);
4518     try {
4519         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4520         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4521         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4522         gtk_tree_selection_select_path (selection, path.gobj());
4523         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4524         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4525     } catch (...) {
4526         if (family && strlen (family))
4527         {
4528             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4529         }
4530     }
4533 void
4534 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4535                                    gpointer          data)
4537     if (g_object_get_data (G_OBJECT (button), "block")) return;
4538     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4539     int prop = GPOINTER_TO_INT(data);
4541     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4542     SPCSSAttr *css = sp_repr_css_attr_new ();
4544     switch (prop)
4545     {
4546         case 0:
4547         {
4548             sp_repr_css_set_property (css, "text-anchor", "start");
4549             sp_repr_css_set_property (css, "text-align", "start");
4550             break;
4551         }
4552         case 1:
4553         {
4554             sp_repr_css_set_property (css, "text-anchor", "middle");
4555             sp_repr_css_set_property (css, "text-align", "center");
4556             break;
4557         }
4559         case 2:
4560         {
4561             sp_repr_css_set_property (css, "text-anchor", "end");
4562             sp_repr_css_set_property (css, "text-align", "end");
4563             break;
4564         }
4566         case 3:
4567         {
4568             sp_repr_css_set_property (css, "text-anchor", "start");
4569             sp_repr_css_set_property (css, "text-align", "justify");
4570             break;
4571         }
4572     }
4574     SPStyle *query =
4575         sp_style_new (SP_ACTIVE_DOCUMENT);
4576     int result_numbers =
4577         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4579     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4580     if (result_numbers == QUERY_STYLE_NOTHING)
4581     {
4582         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4583     }
4585     sp_style_unref(query);
4587     sp_desktop_set_style (desktop, css, true, true);
4588     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4589                                    _("Text: Change alignment"));
4590     sp_repr_css_attr_unref (css);
4592     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4595 void
4596 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4597                                gpointer          data)
4599     if (g_object_get_data (G_OBJECT (button), "block")) return;
4601     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4602     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4603     int          prop       = GPOINTER_TO_INT(data);
4604     bool         active     = gtk_toggle_button_get_active (button);
4606     SPStyle *query =
4607         sp_style_new (SP_ACTIVE_DOCUMENT);
4609     int result_fontspec =
4610         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4612     int result_family =
4613         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4615     int result_style =
4616         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4618     int result_numbers =
4619         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4621     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4622     Glib::ustring newFontSpec = "";
4624     if (fontSpec.empty()) {
4625         // Construct a new font specification if it does not yet exist
4626         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4627         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4628         fontFromStyle->Unref();
4629     }
4631     switch (prop)
4632     {
4633         case 0:
4634         {
4635             if (!fontSpec.empty()) {
4636                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4637             }
4638             if (fontSpec != newFontSpec) {
4639                 // Don't even set the bold if the font didn't exist on the system
4640                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4641             }
4642             break;
4643         }
4645         case 1:
4646         {
4647             if (!fontSpec.empty()) {
4648                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4649             }
4650             if (fontSpec != newFontSpec) {
4651                 // Don't even set the italic if the font didn't exist on the system
4652                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4653             }
4654             break;
4655         }
4656     }
4658     if (!newFontSpec.empty()) {
4659         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4660     }
4662     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4663     if (result_fontspec == QUERY_STYLE_NOTHING)
4664     {
4665         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4666     }
4668     sp_style_unref(query);
4670     sp_desktop_set_style (desktop, css, true, true);
4671     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4672                                    _("Text: Change font style"));
4673     sp_repr_css_attr_unref (css);
4675     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4678 void
4679 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4680                                      gpointer         data)
4682     if (g_object_get_data (G_OBJECT (button), "block")) {
4683         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4684         return;
4685     }
4687     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4688     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4689     int          prop       = GPOINTER_TO_INT(data);
4691     switch (prop)
4692     {
4693         case 0:
4694         {
4695             sp_repr_css_set_property (css, "writing-mode", "lr");
4696             break;
4697         }
4699         case 1:
4700         {
4701             sp_repr_css_set_property (css, "writing-mode", "tb");
4702             break;
4703         }
4704     }
4706     SPStyle *query =
4707         sp_style_new (SP_ACTIVE_DOCUMENT);
4708     int result_numbers =
4709         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4711     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4712     if (result_numbers == QUERY_STYLE_NOTHING)
4713     {
4714         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4715     }
4717     sp_desktop_set_style (desktop, css, true, true);
4718     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4719                                    _("Text: Change orientation"));
4720     sp_repr_css_attr_unref (css);
4722     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4725 gboolean
4726 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4728     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4729     if (!desktop) return FALSE;
4731     switch (get_group0_keyval (event)) {
4732         case GDK_Escape: // defocus
4733             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4734             sp_text_toolbox_selection_changed (NULL, tbl); // update
4735             return TRUE; // I consumed the event
4736             break;
4737     }
4738     return FALSE;
4741 gboolean
4742 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4744     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4745     if (!desktop) return FALSE;
4747     switch (get_group0_keyval (event)) {
4748         case GDK_KP_Enter:
4749         case GDK_Return:
4750         case GDK_Escape: // defocus
4751             gtk_widget_hide (w);
4752             popdown_visible = false;
4753             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4754             return TRUE; // I consumed the event
4755             break;
4756         case GDK_w:
4757         case GDK_W:
4758             if (event->state & GDK_CONTROL_MASK) {
4759                 gtk_widget_hide (w);
4760                 popdown_visible = false;
4761                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4762                 return TRUE; // I consumed the event
4763             }
4764             break;
4765     }
4766     return FALSE;
4770 void
4771 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4772                                GObject     *tbl)
4774     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4776     if (g_object_get_data (tbl, "size-block")) return;
4778     // If this is not from selecting a size in the list (in which case get_active will give the
4779     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4780     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4781     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4782     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4783         return;
4785     gchar *endptr;
4786     gdouble value = -1;
4787     char *text = gtk_combo_box_get_active_text (cbox);
4788     if (text) {
4789         value = g_strtod (text, &endptr);
4790         if (endptr == text) // conversion failed, non-numeric input
4791             value = -1;
4792         free (text);
4793     }
4794     if (value <= 0) {
4795         return; // could not parse value
4796     }
4798     SPCSSAttr *css = sp_repr_css_attr_new ();
4799     Inkscape::CSSOStringStream osfs;
4800     osfs << value;
4801     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4803     SPStyle *query =
4804         sp_style_new (SP_ACTIVE_DOCUMENT);
4805     int result_numbers =
4806         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4808     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4809     if (result_numbers == QUERY_STYLE_NOTHING)
4810     {
4811         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4812     }
4814     sp_style_unref(query);
4816     sp_desktop_set_style (desktop, css, true, true);
4817     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4818                                    _("Text: Change font size"));
4819     sp_repr_css_attr_unref (css);
4821     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4824 gboolean
4825 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4827     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4828     if (!desktop) return FALSE;
4830     if (!g_object_get_data (tbl, "esc-pressed")) {
4831         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4832         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4833         sp_text_toolbox_size_changed (cbox, tbl);
4834         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4835     }
4836     return FALSE; // I consumed the event
4840 gboolean
4841 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4843     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4844     if (!desktop) return FALSE;
4846     switch (get_group0_keyval (event)) {
4847         case GDK_Escape: // defocus
4848             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4849             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4850             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4851             return TRUE; // I consumed the event
4852             break;
4853         case GDK_Return: // defocus
4854         case GDK_KP_Enter:
4855             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4856             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4857             sp_text_toolbox_size_changed (cbox, tbl);
4858             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4859             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4860             return TRUE; // I consumed the event
4861             break;
4862     }
4863     return FALSE;
4866 void
4867 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4868                                          GObject            *tbl)
4870     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4871     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4872     int x, y;
4874     if (!popdown_visible)
4875     {
4876         gdk_window_get_origin (widget->window, &x, &y);
4877         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4878         gtk_widget_show_all (popdown);
4879         //sp_transientize (popdown);
4881         gdk_pointer_grab (widget->window, TRUE,
4882                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4883                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4884                                         GDK_POINTER_MOTION_MASK),
4885                           NULL, NULL, GDK_CURRENT_TIME);
4887         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4889         popdown_visible = true;
4890     }
4891     else
4892     {
4893         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4894         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4895         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4896         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4897         gtk_widget_hide (popdown);
4898         popdown_visible = false;
4899     }
4902 gboolean
4903 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4904                                  GdkEventFocus    */*event*/,
4905                                  GObject          */*tbl*/)
4907     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4908     return FALSE;
4911 gboolean
4912 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4913                                    GdkEventFocus    */*event*/,
4914                                    GObject          */*tbl*/)
4916     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4918     if (popdown_hasfocus) {
4919         gtk_widget_hide (popdown);
4920         popdown_hasfocus = false;
4921         popdown_visible = false;
4922         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4923         return TRUE;
4924     }
4925     return FALSE;
4928 gboolean
4929 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
4930                                    GdkEventFocus    */*event*/,
4931                                    GObject          */*tbl*/)
4933     popdown_hasfocus = true;
4934     return TRUE;
4938 void
4939 cell_data_func  (GtkTreeViewColumn */*column*/,
4940                  GtkCellRenderer   *cell,
4941                  GtkTreeModel      *tree_model,
4942                  GtkTreeIter       *iter,
4943                  gpointer           /*data*/)
4945     char        *family,
4946         *family_escaped,
4947         *sample_escaped;
4949     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4951     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4953     family_escaped = g_markup_escape_text (family, -1);
4954     sample_escaped = g_markup_escape_text (sample, -1);
4956     std::stringstream markup;
4957     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4958     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4960     free (family);
4961     free (family_escaped);
4962     free (sample_escaped);
4965 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4966     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4967     if (completion) {
4968         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4969         g_object_unref (completion);
4970     }
4973 GtkWidget*
4974 sp_text_toolbox_new (SPDesktop *desktop)
4976     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4978     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4979     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4981     GtkTooltips *tt = gtk_tooltips_new();
4982     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4984     ////////////Family
4985     //Window
4986     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4987     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4989     //Entry
4990     GtkWidget           *entry = gtk_entry_new ();
4991     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4992     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4993     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4994     gtk_entry_completion_set_text_column (completion, 0);
4995     gtk_entry_completion_set_minimum_key_length (completion, 1);
4996     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4997     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4998     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4999     aux_toolbox_space (tbl, 1);
5000     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5001     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5003     //Button
5004     GtkWidget   *button = gtk_button_new ();
5005     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5006     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5008     //Popdown
5009     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5010     GtkWidget           *treeview = gtk_tree_view_new ();
5012     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5013     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5014     gtk_tree_view_column_pack_start (column, cell, FALSE);
5015     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5016     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5017     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5019     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5020     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5021     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5023     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5025     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5026     gtk_container_add (GTK_CONTAINER (sw), treeview);
5028     gtk_container_add (GTK_CONTAINER (window), sw);
5029     gtk_widget_set_size_request (window, 300, 450);
5031     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5032     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5033     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5035     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5037     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5038     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5039     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5041     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5042     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5044     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5045     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5046     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5047     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5048     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5050     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
5051     aux_toolbox_space (tbl, 1);
5052     GtkWidget *box = gtk_event_box_new ();
5053     gtk_container_add (GTK_CONTAINER (box), image);
5054     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5055     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5056     GtkTooltips *tooltips = gtk_tooltips_new ();
5057     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5058     gtk_widget_hide (GTK_WIDGET (box));
5059     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5061     ////////////Size
5062     const char *sizes[] = {
5063         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5064         "16", "18", "20", "22", "24", "28",
5065         "32", "36", "40", "48", "56", "64", "72", "144"
5066     };
5068     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5069     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5070     gtk_widget_set_size_request (cbox, 80, -1);
5071     aux_toolbox_space (tbl, 1);
5072     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5073     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5074     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5075     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5076     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5078     //spacer
5079     aux_toolbox_space (tbl, 4);
5080     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5082     ////////////Text anchor
5083     GtkWidget *group   = gtk_radio_button_new (NULL);
5084     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5085     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5087     // left
5088     GtkWidget *rbutton = group;
5089     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5090     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5091     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5093     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5094     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5095     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5096     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5098     // center
5099     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5100     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5101     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
5102     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5104     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5105     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5106     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5107     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5109     // right
5110     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5111     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5112     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5113     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5115     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5116     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5117     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5118     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5120     // fill
5121     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5122     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5123     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5124     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5126     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5127     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5128     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5129     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5131     aux_toolbox_space (tbl, 1);
5132     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5134     //spacer
5135     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5137     ////////////Text style
5138     row = gtk_hbox_new (FALSE, 4);
5140     // bold
5141     rbutton = gtk_toggle_button_new ();
5142     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5143     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5144     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5145     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5147     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5148     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5149     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5151     // italic
5152     rbutton = gtk_toggle_button_new ();
5153     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5154     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5155     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5156     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5158     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5159     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5160     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5162     aux_toolbox_space (tbl, 1);
5163     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5165     //spacer
5166     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5168     ////////////Text orientation
5169     group   = gtk_radio_button_new (NULL);
5170     row     = gtk_hbox_new (FALSE, 4);
5171     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5173     // horizontal
5174     rbutton = group;
5175     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5176     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5177     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5178     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5180     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5181     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5182     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5184     // vertical
5185     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5186     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5187     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5188     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5189     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5191     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5192     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5193     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5194     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5197     //watch selection
5198     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5200     sigc::connection *c_selection_changed =
5201         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5202                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5203     pool->add_connection ("selection-changed", c_selection_changed);
5205     sigc::connection *c_selection_modified =
5206         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5207                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5208     pool->add_connection ("selection-modified", c_selection_modified);
5210     sigc::connection *c_subselection_changed =
5211         new sigc::connection (desktop->connectToolSubselectionChanged
5212                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5213     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5215     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5218     gtk_widget_show_all (tbl);
5219     return tbl;
5221 } // end of sp_text_toolbox_new()
5223 }//<unnamed> namespace
5226 //#########################
5227 //##      Connector      ##
5228 //#########################
5230 static void sp_connector_path_set_avoid(void)
5232     cc_selection_set_avoid(true);
5236 static void sp_connector_path_set_ignore(void)
5238     cc_selection_set_avoid(false);
5243 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5245     // quit if run by the _changed callbacks
5246     if (g_object_get_data( tbl, "freeze" )) {
5247         return;
5248     }
5250     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5251     SPDocument *doc = sp_desktop_document(desktop);
5253     if (!sp_document_get_undo_sensitive(doc))
5254     {
5255         return;
5256     }
5258     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5260     if ( repr->attribute("inkscape:connector-spacing") ) {
5261         gdouble priorValue = gtk_adjustment_get_value(adj);
5262         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5263         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5264             return;
5265         }
5266     } else if ( adj->value == defaultConnSpacing ) {
5267         return;
5268     }
5270     // in turn, prevent callbacks from responding
5271     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5273     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5274     SP_OBJECT(desktop->namedview)->updateRepr();
5276     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5277     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5278         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5279         NR::Matrix m = NR::identity();
5280         avoid_item_move(&m, item);
5281     }
5283     if (items) {
5284         g_slist_free(items);
5285     }
5287     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5288             _("Change connector spacing"));
5290     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5292     spinbutton_defocus(GTK_OBJECT(tbl));
5295 static void sp_connector_graph_layout(void)
5297     if (!SP_ACTIVE_DESKTOP) return;
5299     // hack for clones, see comment in align-and-distribute.cpp
5300     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5301     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5303     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5305     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5307     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5310 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5312     if ( gtk_toggle_action_get_active( act ) ) {
5313         prefs_set_string_attribute("tools.connector", "directedlayout",
5314                 "true");
5315     } else {
5316         prefs_set_string_attribute("tools.connector", "directedlayout",
5317                 "false");
5318     }
5321 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5323     if ( gtk_toggle_action_get_active( act ) ) {
5324         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5325                 "true");
5326     } else {
5327         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5328                 "false");
5329     }
5333 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5335     prefs_set_double_attribute("tools.connector", "length", adj->value);
5336     spinbutton_defocus(GTK_OBJECT(tbl));
5339 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5340                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5341                                             bool /*is_interactive*/, gpointer data)
5343     GtkWidget *tbl = GTK_WIDGET(data);
5345     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5346         return;
5347     }
5348     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5349         return;
5350     }
5352     GtkAdjustment *adj = (GtkAdjustment*)
5353             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5354     gdouble spacing = defaultConnSpacing;
5355     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5357     gtk_adjustment_set_value(adj, spacing);
5361 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5362     NULL, /* child_added */
5363     NULL, /* child_removed */
5364     connector_tb_event_attr_changed,
5365     NULL, /* content_changed */
5366     NULL  /* order_changed */
5367 };
5370 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5372     {
5373         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5374                                           _("Avoid"),
5375                                           _("Make connectors avoid selected objects"),
5376                                           "connector_avoid",
5377                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5378         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5379         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5380     }
5382     {
5383         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5384                                           _("Ignore"),
5385                                           _("Make connectors ignore selected objects"),
5386                                           "connector_ignore",
5387                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5388         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5389         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5390     }
5392     EgeAdjustmentAction* eact = 0;
5394     // Spacing spinbox
5395     eact = create_adjustment_action( "ConnectorSpacingAction",
5396                                      _("Connector Spacing"), _("Spacing:"),
5397                                      _("The amount of space left around objects by auto-routing connectors"),
5398                                      "tools.connector", "spacing", defaultConnSpacing,
5399                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5400                                      0, 100, 1.0, 10.0,
5401                                      0, 0, 0,
5402                                      connector_spacing_changed, 1, 0 );
5403     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5405     // Graph (connector network) layout
5406     {
5407         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5408                                           _("Graph"),
5409                                           _("Nicely arrange selected connector network"),
5410                                           "graph_layout",
5411                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5412         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5413         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5414     }
5416     // Default connector length spinbox
5417     eact = create_adjustment_action( "ConnectorLengthAction",
5418                                      _("Connector Length"), _("Length:"),
5419                                      _("Ideal length for connectors when layout is applied"),
5420                                      "tools.connector", "length", 100,
5421                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5422                                      10, 1000, 10.0, 100.0,
5423                                      0, 0, 0,
5424                                      connector_length_changed, 1, 0 );
5425     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5428     // Directed edges toggle button
5429     {
5430         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5431                                                       _("Downwards"),
5432                                                       _("Make connectors with end-markers (arrows) point downwards"),
5433                                                       "directed_graph",
5434                                                       Inkscape::ICON_SIZE_DECORATION );
5435         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5437         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5438         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5439                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5441         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5442     }
5444     // Avoid overlaps toggle button
5445     {
5446         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5447                                                       _("Remove overlaps"),
5448                                                       _("Do not allow overlapping shapes"),
5449                                                       "remove_overlaps",
5450                                                       Inkscape::ICON_SIZE_DECORATION );
5451         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5453         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5454         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5455                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5457         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5458     }
5460     // Code to watch for changes to the connector-spacing attribute in
5461     // the XML.
5462     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5463     g_assert(repr != NULL);
5465     purge_repr_listener( holder, holder );
5467     if (repr) {
5468         g_object_set_data( holder, "repr", repr );
5469         Inkscape::GC::anchor(repr);
5470         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5471         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5472     }
5473 } // end of sp_connector_toolbox_prep()
5476 //#########################
5477 //##     Paintbucket     ##
5478 //#########################
5480 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5482     gint channels = ege_select_one_action_get_active( act );
5483     flood_channels_set_channels( channels );
5486 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5488     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5491 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5493     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5496 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5498     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5499     SPUnit const *unit = tracker->getActiveUnit();
5501     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5503     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5506 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5508     // FIXME: make defaults settable via Inkscape Options
5509     struct KeyValue {
5510         char const *key;
5511         double value;
5512     } const key_values[] = {
5513         {"threshold", 15},
5514         {"offset", 0.0}
5515     };
5517     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5518         KeyValue const &kv = key_values[i];
5519         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5520         if ( adj ) {
5521             gtk_adjustment_set_value(adj, kv.value);
5522         }
5523     }
5525     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5526     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5527     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5528     ege_select_one_action_set_active( autogap_action, 0 );
5531 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5533     EgeAdjustmentAction* eact = 0;
5535     {
5536         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5538         GList* items = 0;
5539         gint count = 0;
5540         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5541         {
5542             GtkTreeIter iter;
5543             gtk_list_store_append( model, &iter );
5544             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5545             count++;
5546         }
5547         g_list_free( items );
5548         items = 0;
5549         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5550         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5551         ege_select_one_action_set_appearance( act1, "compact" );
5552         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5553         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5554         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5555         g_object_set_data( holder, "channels_action", act1 );
5556     }
5558     // Spacing spinbox
5559     {
5560         eact = create_adjustment_action(
5561             "ThresholdAction",
5562             _("Fill Threshold"), _("Threshold:"),
5563             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5564             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5565             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5566             0, 0, 0,
5567             paintbucket_threshold_changed, 1, 0 );
5569         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5570     }
5572     // Create the units menu.
5573     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5574     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5575     if (stored_unit)
5576         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5577     g_object_set_data( holder, "tracker", tracker );
5578     {
5579         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5580         gtk_action_group_add_action( mainActions, act );
5581     }
5583     // Offset spinbox
5584     {
5585         eact = create_adjustment_action(
5586             "OffsetAction",
5587             _("Grow/shrink by"), _("Grow/shrink by:"),
5588             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5589             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5590             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5591             0, 0, 0,
5592             paintbucket_offset_changed, 1, 2);
5593         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5595         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5596     }
5598     /* Auto Gap */
5599     {
5600         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5602         GList* items = 0;
5603         gint count = 0;
5604         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5605         {
5606             GtkTreeIter iter;
5607             gtk_list_store_append( model, &iter );
5608             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5609             count++;
5610         }
5611         g_list_free( items );
5612         items = 0;
5613         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5614         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5615         ege_select_one_action_set_appearance( act2, "compact" );
5616         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5617         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5618         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5619         g_object_set_data( holder, "autogap_action", act2 );
5620     }
5622     /* Reset */
5623     {
5624         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5625                                           _("Defaults"),
5626                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5627                                           GTK_STOCK_CLEAR );
5628         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5629         gtk_action_group_add_action( mainActions, act );
5630         gtk_action_set_sensitive( act, TRUE );
5631     }
5635 /*
5636   Local Variables:
5637   mode:c++
5638   c-file-style:"stroustrup"
5639   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5640   indent-tabs-mode:nil
5641   fill-column:99
5642   End:
5643 */
5644 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :