Code

Really fix compile (patch by Krzysztof KosiƄski)
[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='NodeJoinSegmentAction' />"
243         "    <toolitem action='NodeDeleteSegmentAction' />"
244         "    <toolitem action='NodeBreakAction' />"
245         "    <separator />"
246         "    <toolitem action='NodeCuspAction' />"
247         "    <toolitem action='NodeSmoothAction' />"
248         "    <toolitem action='NodeSymmetricAction' />"
249         "    <separator />"
250         "    <toolitem action='NodeLineAction' />"
251         "    <toolitem action='NodeCurveAction' />"
252         "    <separator />"
253         "    <toolitem action='ObjectToPath' />"
254         "    <toolitem action='StrokeToPath' />"
255         "    <separator />"
256         "    <toolitem action='NodeXAction' />"
257         "    <toolitem action='NodeYAction' />"
258         "    <toolitem action='NodeUnitsAction' />"
259         "    <separator />"
260         "    <toolitem action='ObjectEditClipPathAction' />"
261         "    <toolitem action='ObjectEditMaskPathAction' />"
262         "    <toolitem action='EditNextLPEParameterAction' />"
263         "    <separator />"
264         "    <toolitem action='NodesShowHandlesAction' />"
265         "    <toolitem action='NodesShowHelperpath' />"
266         "  </toolbar>"
268         "  <toolbar name='TweakToolbar'>"
269         "    <toolitem action='TweakWidthAction' />"
270         "    <separator />"
271         "    <toolitem action='TweakForceAction' />"
272         "    <toolitem action='TweakPressureAction' />"
273         "    <separator />"
274         "    <toolitem action='TweakModeAction' />"
275         "    <separator />"
276         "    <toolitem action='TweakFidelityAction' />"
277         "    <separator />"
278         "    <toolitem action='TweakChannelsLabel' />"
279         "    <toolitem action='TweakDoH' />"
280         "    <toolitem action='TweakDoS' />"
281         "    <toolitem action='TweakDoL' />"
282         "    <toolitem action='TweakDoO' />"
283         "  </toolbar>"
285         "  <toolbar name='ZoomToolbar'>"
286         "    <toolitem action='ZoomIn' />"
287         "    <toolitem action='ZoomOut' />"
288         "    <separator />"
289         "    <toolitem action='Zoom1:0' />"
290         "    <toolitem action='Zoom1:2' />"
291         "    <toolitem action='Zoom2:1' />"
292         "    <separator />"
293         "    <toolitem action='ZoomSelection' />"
294         "    <toolitem action='ZoomDrawing' />"
295         "    <toolitem action='ZoomPage' />"
296         "    <toolitem action='ZoomPageWidth' />"
297         "    <separator />"
298         "    <toolitem action='ZoomPrev' />"
299         "    <toolitem action='ZoomNext' />"
300         "  </toolbar>"
302         "  <toolbar name='StarToolbar'>"
303         "    <separator />"
304         "    <toolitem action='StarStateAction' />"
305         "    <separator />"
306         "    <toolitem action='FlatAction' />"
307         "    <separator />"
308         "    <toolitem action='MagnitudeAction' />"
309         "    <toolitem action='SpokeAction' />"
310         "    <toolitem action='RoundednessAction' />"
311         "    <toolitem action='RandomizationAction' />"
312         "    <separator />"
313         "    <toolitem action='StarResetAction' />"
314         "  </toolbar>"
316         "  <toolbar name='RectToolbar'>"
317         "    <toolitem action='RectStateAction' />"
318         "    <toolitem action='RectWidthAction' />"
319         "    <toolitem action='RectHeightAction' />"
320         "    <toolitem action='RadiusXAction' />"
321         "    <toolitem action='RadiusYAction' />"
322         "    <toolitem action='RectUnitsAction' />"
323         "    <separator />"
324         "    <toolitem action='RectResetAction' />"
325         "  </toolbar>"
327         "  <toolbar name='3DBoxToolbar'>"
328         "    <toolitem action='3DBoxAngleXAction' />"
329         "    <toolitem action='3DBoxVPXStateAction' />"
330         "    <separator />"
331         "    <toolitem action='3DBoxAngleYAction' />"
332         "    <toolitem action='3DBoxVPYStateAction' />"
333         "    <separator />"
334         "    <toolitem action='3DBoxAngleZAction' />"
335         "    <toolitem action='3DBoxVPZStateAction' />"
336         "  </toolbar>"
338         "  <toolbar name='SpiralToolbar'>"
339         "    <toolitem action='SpiralStateAction' />"
340         "    <toolitem action='SpiralRevolutionAction' />"
341         "    <toolitem action='SpiralExpansionAction' />"
342         "    <toolitem action='SpiralT0Action' />"
343         "    <separator />"
344         "    <toolitem action='SpiralResetAction' />"
345         "  </toolbar>"
347         "  <toolbar name='PenToolbar'>"
348         "  </toolbar>"
350         "  <toolbar name='PencilToolbar'>"
351         "  </toolbar>"
353         "  <toolbar name='CalligraphyToolbar'>"
354         "    <separator />"
355         "    <toolitem action='SetProfileAction'/>"
356         "    <toolitem action='SaveDeleteProfileAction'/>"
357         "    <separator />"
358         "    <toolitem action='CalligraphyWidthAction' />"
359         "    <toolitem action='PressureAction' />"
360         "    <toolitem action='TraceAction' />"
361         "    <toolitem action='ThinningAction' />"
362         "    <separator />"
363         "    <toolitem action='AngleAction' />"
364         "    <toolitem action='TiltAction' />"
365         "    <toolitem action='FixationAction' />"
366         "    <separator />"
367         "    <toolitem action='CapRoundingAction' />"
368         "    <separator />"
369         "    <toolitem action='TremorAction' />"
370         "    <toolitem action='WiggleAction' />"
371         "    <toolitem action='MassAction' />"
372         "    <separator />"
373         "  </toolbar>"
375         "  <toolbar name='ArcToolbar'>"
376         "    <toolitem action='ArcStateAction' />"
377         "    <separator />"
378         "    <toolitem action='ArcStartAction' />"
379         "    <toolitem action='ArcEndAction' />"
380         "    <separator />"
381         "    <toolitem action='ArcOpenAction' />"
382         "    <separator />"
383         "    <toolitem action='ArcResetAction' />"
384         "    <separator />"
385         "  </toolbar>"
387         "  <toolbar name='PaintbucketToolbar'>"
388         "    <toolitem action='ChannelsAction' />"
389         "    <separator />"
390         "    <toolitem action='ThresholdAction' />"
391         "    <separator />"
392         "    <toolitem action='OffsetAction' />"
393         "    <toolitem action='PaintbucketUnitsAction' />"
394         "    <separator />"
395         "    <toolitem action='AutoGapAction' />"
396         "    <separator />"
397         "    <toolitem action='PaintbucketResetAction' />"
398         "  </toolbar>"
400         "  <toolbar name='DropperToolbar'>"
401         "    <toolitem action='DropperOpacityAction' />"
402         "    <toolitem action='DropperPickAlphaAction' />"
403         "    <toolitem action='DropperSetAlphaAction' />"
404         "  </toolbar>"
406         "  <toolbar name='ConnectorToolbar'>"
407         "    <toolitem action='ConnectorAvoidAction' />"
408         "    <toolitem action='ConnectorIgnoreAction' />"
409         "    <toolitem action='ConnectorSpacingAction' />"
410         "    <toolitem action='ConnectorGraphAction' />"
411         "    <toolitem action='ConnectorLengthAction' />"
412         "    <toolitem action='ConnectorDirectedAction' />"
413         "    <toolitem action='ConnectorOverlapAction' />"
414         "  </toolbar>"
416         "</ui>"
419 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
421 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
423 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
424 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
426 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
427 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
429 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
430 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
432 /* Global text entry widgets necessary for update */
433 /* GtkWidget *dropper_rgb_entry,
434           *dropper_opacity_entry ; */
435 // should be made a private member once this is converted to class
437 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
438     connection->disconnect();
439     delete connection;
442 static void purge_repr_listener( GObject* obj, GObject* tbl )
444     (void)obj;
445     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
446     if (oldrepr) { // remove old listener
447         sp_repr_remove_listener_by_data(oldrepr, tbl);
448         Inkscape::GC::release(oldrepr);
449         oldrepr = 0;
450         g_object_set_data( tbl, "repr", NULL );
451     }
454 GtkWidget *
455 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
456                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
457                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
459     SPAction *action = verb->get_action(view);
460     if (!action) return NULL;
462     SPAction *doubleclick_action;
463     if (doubleclick_verb)
464         doubleclick_action = doubleclick_verb->get_action(view);
465     else
466         doubleclick_action = NULL;
468     /* fixme: Handle sensitive/unsensitive */
469     /* fixme: Implement sp_button_new_from_action */
470     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
471     gtk_widget_show(b);
472     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
474     return b;
477 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
478                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
480     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
483 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
484                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
486     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
490 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
492     SPAction* targetAction = SP_ACTION(user_data);
493     if ( targetAction ) {
494         sp_action_perform( targetAction, NULL );
495     }
498 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
500     if ( data ) {
501         GtkAction* act = GTK_ACTION(data);
502         gtk_action_set_sensitive( act, sensitive );
503     }
506 static SPActionEventVector action_event_vector = {
507     {NULL},
508     NULL,
509     NULL,
510     sp_action_action_set_sensitive,
511     NULL,
512     NULL
513 };
515 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
517     GtkAction* act = 0;
519     SPAction* targetAction = verb->get_action(view);
520     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
521     act = GTK_ACTION(inky);
522     gtk_action_set_sensitive( act, targetAction->sensitive );
524     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
526     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
527     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
529     return act;
532 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
534     Inkscape::UI::View::View *view = desktop;
535     gint verbsToUse[] = {
536         // disabled until we have icons for them:
537         //find
538         //SP_VERB_EDIT_TILE,
539         //SP_VERB_EDIT_UNTILE,
540         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
541         SP_VERB_DIALOG_DISPLAY,
542         SP_VERB_DIALOG_FILL_STROKE,
543         SP_VERB_DIALOG_NAMEDVIEW,
544         SP_VERB_DIALOG_TEXT,
545         SP_VERB_DIALOG_XML_EDITOR,
546         SP_VERB_EDIT_CLONE,
547         SP_VERB_EDIT_COPY,
548         SP_VERB_EDIT_CUT,
549         SP_VERB_EDIT_DUPLICATE,
550         SP_VERB_EDIT_PASTE,
551         SP_VERB_EDIT_REDO,
552         SP_VERB_EDIT_UNDO,
553         SP_VERB_EDIT_UNLINK_CLONE,
554         SP_VERB_FILE_EXPORT,
555         SP_VERB_FILE_IMPORT,
556         SP_VERB_FILE_NEW,
557         SP_VERB_FILE_OPEN,
558         SP_VERB_FILE_PRINT,
559         SP_VERB_FILE_SAVE,
560         SP_VERB_OBJECT_TO_CURVE,
561         SP_VERB_SELECTION_GROUP,
562         SP_VERB_SELECTION_OUTLINE,
563         SP_VERB_SELECTION_UNGROUP,
564         SP_VERB_ZOOM_1_1,
565         SP_VERB_ZOOM_1_2,
566         SP_VERB_ZOOM_2_1,
567         SP_VERB_ZOOM_DRAWING,
568         SP_VERB_ZOOM_IN,
569         SP_VERB_ZOOM_NEXT,
570         SP_VERB_ZOOM_OUT,
571         SP_VERB_ZOOM_PAGE,
572         SP_VERB_ZOOM_PAGE_WIDTH,
573         SP_VERB_ZOOM_PREV,
574         SP_VERB_ZOOM_SELECTION,
575     };
577     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
578     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
580     static std::map<SPDesktop*, GtkActionGroup*> groups;
581     GtkActionGroup* mainActions = 0;
582     if ( groups.find(desktop) != groups.end() ) {
583         mainActions = groups[desktop];
584     }
586     if ( !mainActions ) {
587         mainActions = gtk_action_group_new("main");
588         groups[desktop] = mainActions;
589     }
591     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
592         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
593         if ( verb ) {
594             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
595                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
596                 gtk_action_group_add_action( mainActions, act );
597             }
598         }
599     }
601     return mainActions;
605 GtkWidget *
606 sp_tool_toolbox_new()
608     GtkTooltips *tt = gtk_tooltips_new();
609     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
611     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
612     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
614     gtk_widget_set_sensitive(tb, FALSE);
616     GtkWidget *hb = gtk_handle_box_new();
617     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
618     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
619     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
621     gtk_container_add(GTK_CONTAINER(hb), tb);
622     gtk_widget_show(GTK_WIDGET(tb));
624     sigc::connection* conn = new sigc::connection;
625     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
627     return hb;
630 static void
631 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
633     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
634     gtk_widget_queue_resize(child);
637 static void
638 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
640     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
641     gtk_widget_queue_resize(child);
644 GtkWidget *
645 sp_aux_toolbox_new()
647     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
649     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
651     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
653     gtk_widget_set_sensitive(tb, FALSE);
655     GtkWidget *hb = gtk_handle_box_new();
656     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
657     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
658     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
660     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
661     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
663     gtk_container_add(GTK_CONTAINER(hb), tb);
664     gtk_widget_show(GTK_WIDGET(tb));
666     sigc::connection* conn = new sigc::connection;
667     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
669     return hb;
672 //####################################
673 //# Commands Bar
674 //####################################
676 GtkWidget *
677 sp_commands_toolbox_new()
679     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
681     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
683     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
684     gtk_widget_set_sensitive(tb, FALSE);
686     GtkWidget *hb = gtk_handle_box_new();
687     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
688     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
689     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
691     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
692     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
694     gtk_container_add(GTK_CONTAINER(hb), tb);
695     gtk_widget_show(GTK_WIDGET(tb));
697     sigc::connection* conn = new sigc::connection;
698     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
700     return hb;
703 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
704                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
705                                                        gchar const *path, gchar const *data, gdouble def,
706                                                        GtkWidget *focusTarget,
707                                                        GtkWidget *us,
708                                                        GObject *dataKludge,
709                                                        gboolean altx, gchar const *altx_mark,
710                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
711                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
712                                                        void (*callback)(GtkAdjustment *, GObject *),
713                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
715     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
716                                                              lower, upper, step, page, page ) );
717     if (us) {
718         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
719     }
721     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
723     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
724     if ( shortLabel ) {
725         g_object_set( act, "short_label", shortLabel, NULL );
726     }
728     if ( (descrCount > 0) && descrLabels && descrValues ) {
729         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
730     }
732     if ( focusTarget ) {
733         ege_adjustment_action_set_focuswidget( act, focusTarget );
734     }
736     if ( altx && altx_mark ) {
737         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
738     }
740     if ( dataKludge ) {
741         g_object_set_data( dataKludge, data, adj );
742     }
744     // Using a cast just to make sure we pass in the right kind of function pointer
745     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
747     return act;
751 //####################################
752 //# node editing callbacks
753 //####################################
755 /**
756  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
757  */
758 static ShapeEditor *get_current_shape_editor()
760     if (!SP_ACTIVE_DESKTOP) {
761         return NULL;
762     }
764     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
766     if (!SP_IS_NODE_CONTEXT(event_context)) {
767         return NULL;
768     }
770     return SP_NODE_CONTEXT(event_context)->shape_editor;
774 void
775 sp_node_path_edit_add(void)
777     ShapeEditor *shape_editor = get_current_shape_editor();
778     if (shape_editor) shape_editor->add_node();
781 void
782 sp_node_path_edit_delete(void)
784     ShapeEditor *shape_editor = get_current_shape_editor();
785     if (shape_editor) shape_editor->delete_nodes();
788 void
789 sp_node_path_edit_delete_segment(void)
791     ShapeEditor *shape_editor = get_current_shape_editor();
792     if (shape_editor) shape_editor->delete_segment();
795 void
796 sp_node_path_edit_break(void)
798     ShapeEditor *shape_editor = get_current_shape_editor();
799     if (shape_editor) shape_editor->break_at_nodes();
802 void
803 sp_node_path_edit_join(void)
805     ShapeEditor *shape_editor = get_current_shape_editor();
806     if (shape_editor) shape_editor->join_nodes();
809 void
810 sp_node_path_edit_join_segment(void)
812     ShapeEditor *shape_editor = get_current_shape_editor();
813     if (shape_editor) shape_editor->join_segments();
816 void
817 sp_node_path_edit_toline(void)
819     ShapeEditor *shape_editor = get_current_shape_editor();
820     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
823 void
824 sp_node_path_edit_tocurve(void)
826     ShapeEditor *shape_editor = get_current_shape_editor();
827     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
830 void
831 sp_node_path_edit_cusp(void)
833     ShapeEditor *shape_editor = get_current_shape_editor();
834     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
837 void
838 sp_node_path_edit_smooth(void)
840     ShapeEditor *shape_editor = get_current_shape_editor();
841     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
844 void
845 sp_node_path_edit_symmetrical(void)
847     ShapeEditor *shape_editor = get_current_shape_editor();
848     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
851 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
852     bool show = gtk_toggle_action_get_active( act );
853     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
854     ShapeEditor *shape_editor = get_current_shape_editor();
855     if (shape_editor) shape_editor->show_handles(show);
858 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
859     bool show = gtk_toggle_action_get_active( act );
860     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
861     ShapeEditor *shape_editor = get_current_shape_editor();
862     if (shape_editor) shape_editor->show_helperpath(show);
865 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
866     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
869 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
870     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
873 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
874     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
877 /* is called when the node selection is modified */
878 static void
879 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
881     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
882     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
883     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
884     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
886     // quit if run by the attr_changed listener
887     if (g_object_get_data( tbl, "freeze" )) {
888         return;
889     }
891     // in turn, prevent listener from responding
892     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
894     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
895     SPUnit const *unit = tracker->getActiveUnit();
897     ShapeEditor *shape_editor = get_current_shape_editor();
898     if (shape_editor && shape_editor->has_nodepath()) {
899         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
900         int n_selected = 0;
901         if (nodepath) {
902             n_selected = nodepath->numSelected();
903         }
905         if (n_selected == 0) {
906             gtk_action_set_sensitive(xact, FALSE);
907             gtk_action_set_sensitive(yact, FALSE);
908         } else {
909             gtk_action_set_sensitive(xact, TRUE);
910             gtk_action_set_sensitive(yact, TRUE);
911             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
912             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
914             if (n_selected == 1) {
915                 NR::Point sel_node = nodepath->singleSelectedCoords();
916                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
917                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
918                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
919                 }
920             } else {
921                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
922                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
923                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
924                     /* Note: Currently x and y will always have a value, even if the coordinates of the
925                        selected nodes don't coincide (in this case we use the coordinates of the center
926                        of the bounding box). So the entries are never set to zero. */
927                     // FIXME: Maybe we should clear the entry if several nodes are selected
928                     //        instead of providing a kind of average value
929                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
930                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
931                 }
932             }
933         }
934     } else {
935         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
936         gtk_action_set_sensitive(xact, FALSE);
937         gtk_action_set_sensitive(yact, FALSE);
938     }
940     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
943 static void
944 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
946     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
948     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
949     SPUnit const *unit = tracker->getActiveUnit();
951     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
952         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
953     }
955     // quit if run by the attr_changed listener
956     if (g_object_get_data( tbl, "freeze" )) {
957         return;
958     }
960     // in turn, prevent listener from responding
961     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
963     ShapeEditor *shape_editor = get_current_shape_editor();
964     if (shape_editor && shape_editor->has_nodepath()) {
965         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
966         if (!strcmp(value_name, "x")) {
967             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
968         }
969         if (!strcmp(value_name, "y")) {
970             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
971         }
972     }
974     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
977 static void
978 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
980     sp_node_path_value_changed(adj, tbl, "x");
983 static void
984 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
986     sp_node_path_value_changed(adj, tbl, "y");
989 void
990 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
992     {
993     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
994     SPItem *item = selection->singleItem();
995     if (item && SP_IS_SHAPE(item)) {
996        LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(item));
997        if (lpeobj) {
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( "NodeJoinSegmentAction",
1081                                           _("Join Segment"),
1082                                           _("Join selected endnodes with a new segment"),
1083                                           "node_join_segment",
1084                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1085         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1086         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1087     }
1089     {
1090         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1091                                           _("Delete Segment"),
1092                                           _("Split path between two non-endpoint nodes"),
1093                                           "node_delete_segment",
1094                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1095         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1096         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1097     }
1099     {
1100         InkAction* inky = ink_action_new( "NodeBreakAction",
1101                                           _("Node Break"),
1102                                           _("Break path at selected nodes"),
1103                                           "node_break",
1104                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1105         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1106         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1107     }
1109     {
1110         InkAction* inky = ink_action_new( "NodeCuspAction",
1111                                           _("Node Cusp"),
1112                                           _("Make selected nodes corner"),
1113                                           "node_cusp",
1114                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1115         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1116         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1117     }
1119     {
1120         InkAction* inky = ink_action_new( "NodeSmoothAction",
1121                                           _("Node Smooth"),
1122                                           _("Make selected nodes smooth"),
1123                                           "node_smooth",
1124                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1125         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1126         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1127     }
1129     {
1130         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1131                                           _("Node Symmetric"),
1132                                           _("Make selected nodes symmetric"),
1133                                           "node_symmetric",
1134                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1135         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1136         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1137     }
1139     {
1140         InkAction* inky = ink_action_new( "NodeLineAction",
1141                                           _("Node Line"),
1142                                           _("Make selected segments lines"),
1143                                           "node_line",
1144                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1145         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1146         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1147     }
1149     {
1150         InkAction* inky = ink_action_new( "NodeCurveAction",
1151                                           _("Node Curve"),
1152                                           _("Make selected segments curves"),
1153                                           "node_curve",
1154                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1155         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1156         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1157     }
1159     {
1160         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1161                                                       _("Show Handles"),
1162                                                       _("Show the Bezier handles of selected nodes"),
1163                                                       "nodes_show_handles",
1164                                                       Inkscape::ICON_SIZE_DECORATION );
1165         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1166         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1167         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1168     }
1170     {
1171         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1172                                                       _("Show Outline"),
1173                                                       _("Show the outline of the path"),
1174                                                       "nodes_show_helperpath",
1175                                                       Inkscape::ICON_SIZE_DECORATION );
1176         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1177         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1178         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1179     }
1181     {
1182         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1183                                           _("Next path effect parameter"),
1184                                           _("Show next path effect parameter for editing"),
1185                                           "edit_next_parameter",
1186                                           Inkscape::ICON_SIZE_DECORATION );
1187         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1188         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1189         g_object_set_data( holder, "nodes_lpeedit", inky);
1190     }
1192     {
1193         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1194                                           _("Edit clipping path"),
1195                                           _("Edit the clipping path of the object"),
1196                                           "nodeedit-clippath",
1197                                           Inkscape::ICON_SIZE_DECORATION );
1198         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1199         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1200         g_object_set_data( holder, "nodes_clippathedit", inky);
1201     }
1203     {
1204         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1205                                           _("Edit mask path"),
1206                                           _("Edit the mask of the object"),
1207                                           "nodeedit-mask",
1208                                           Inkscape::ICON_SIZE_DECORATION );
1209         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1210         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1211         g_object_set_data( holder, "nodes_maskedit", inky);
1212     }
1214     /* X coord of selected node(s) */
1215     {
1216         EgeAdjustmentAction* eact = 0;
1217         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1218         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1219         eact = create_adjustment_action( "NodeXAction",
1220                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1221                                          "tools.nodes", "Xcoord", 0,
1222                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1223                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1224                                          labels, values, G_N_ELEMENTS(labels),
1225                                          sp_node_path_x_value_changed );
1226         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1227         g_object_set_data( holder, "nodes_x_action", eact );
1228         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1229         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1230     }
1232     /* Y coord of selected node(s) */
1233     {
1234         EgeAdjustmentAction* eact = 0;
1235         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1236         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1237         eact = create_adjustment_action( "NodeYAction",
1238                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1239                                          "tools.nodes", "Ycoord", 0,
1240                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1241                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1242                                          labels, values, G_N_ELEMENTS(labels),
1243                                          sp_node_path_y_value_changed );
1244         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1245         g_object_set_data( holder, "nodes_y_action", eact );
1246         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1247         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1248     }
1250     // add the units menu
1251     {
1252         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1253         gtk_action_group_add_action( mainActions, act );
1254     }
1257     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1259     //watch selection
1260     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1262     sigc::connection *c_selection_changed =
1263         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1264                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1265     pool->add_connection ("selection-changed", c_selection_changed);
1267     sigc::connection *c_selection_modified =
1268         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1269                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1270     pool->add_connection ("selection-modified", c_selection_modified);
1272     sigc::connection *c_subselection_changed =
1273         new sigc::connection (desktop->connectToolSubselectionChanged
1274                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1275     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1277     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1279     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1280 } // end of sp_node_toolbox_prep()
1283 //########################
1284 //##    Zoom Toolbox    ##
1285 //########################
1287 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1289     // no custom GtkAction setup needed
1290 } // end of sp_zoom_toolbox_prep()
1292 void
1293 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1295     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")));
1299 void
1300 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1302     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")));
1305 void
1306 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1308     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")));
1311 static void
1312 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1314     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1315     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1317     if (old_desktop) {
1318         GList *children, *iter;
1320         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1321         for ( iter = children ; iter ; iter = iter->next ) {
1322             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1323         }
1324         g_list_free(children);
1325     }
1327     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1329     if (desktop) {
1330         gtk_widget_set_sensitive(toolbox, TRUE);
1331         setup_func(toolbox, desktop);
1332         update_func(desktop, desktop->event_context, toolbox);
1333         *conn = desktop->connectEventContextChanged
1334             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1335     } else {
1336         gtk_widget_set_sensitive(toolbox, FALSE);
1337     }
1339 } // end of toolbox_set_desktop()
1342 static void
1343 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1345     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1346     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1347     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1348         // "toolbox.tools" was not set. Fallback to older value
1349         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1351         // Copy the setting forwards
1352         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1353     }
1354     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1356     for (int i = 0 ; tools[i].type_name ; i++ ) {
1357         GtkWidget *button =
1358             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1359                                                               SP_BUTTON_TYPE_TOGGLE,
1360                                                               Inkscape::Verb::get(tools[i].verb),
1361                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1362                                                               desktop,
1363                                                               tooltips );
1365         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1366                            (gpointer)button );
1367     }
1371 static void
1372 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1374     gchar const *const tname = ( eventcontext
1375                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1376                                  : NULL );
1377     for (int i = 0 ; tools[i].type_name ; i++ ) {
1378         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1379         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1380     }
1383 static void
1384 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1386     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1387     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1388     GtkUIManager* mgr = gtk_ui_manager_new();
1389     GError* errVal = 0;
1390     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1391     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1393     std::map<std::string, GtkWidget*> dataHolders;
1395     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1396         if ( aux_toolboxes[i].prep_func ) {
1397             // converted to GtkActions and UIManager
1399             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1400             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1401             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1402             dataHolders[aux_toolboxes[i].type_name] = kludge;
1403             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1404         } else {
1406             GtkWidget *sub_toolbox = 0;
1407             if (aux_toolboxes[i].create_func == NULL)
1408                 sub_toolbox = sp_empty_toolbox_new(desktop);
1409             else {
1410                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1411             }
1413             gtk_size_group_add_widget( grouper, sub_toolbox );
1415             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1416             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1418         }
1419     }
1421     // Second pass to create toolbars *after* all GtkActions are created
1422     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1423         if ( aux_toolboxes[i].prep_func ) {
1424             // converted to GtkActions and UIManager
1426             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1428             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1429             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1431             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1432             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1433             g_free( tmp );
1434             tmp = 0;
1436             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1437             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1438             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1439                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1440             }
1441             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1444             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1446             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1447                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1448                 swatch->setDesktop( desktop );
1449                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1450                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1451                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1452                 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 );
1453             }
1455             gtk_widget_show_all( holder );
1456             sp_set_font_size_smaller( holder );
1458             gtk_size_group_add_widget( grouper, holder );
1460             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1461             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1462         }
1463     }
1465     g_object_unref( G_OBJECT(grouper) );
1468 static void
1469 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1471     gchar const *tname = ( eventcontext
1472                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1473                            : NULL );
1474     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1475         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1476         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1477             gtk_widget_show_all(sub_toolbox);
1478             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1479         } else {
1480             gtk_widget_hide(sub_toolbox);
1481         }
1482     }
1485 static void
1486 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1488     gchar const * descr =
1489         "<ui>"
1490         "  <toolbar name='CommandsToolbar'>"
1491         "    <toolitem action='FileNew' />"
1492         "    <toolitem action='FileOpen' />"
1493         "    <toolitem action='FileSave' />"
1494         "    <toolitem action='FilePrint' />"
1495         "    <separator />"
1496         "    <toolitem action='FileImport' />"
1497         "    <toolitem action='FileExport' />"
1498         "    <separator />"
1499         "    <toolitem action='EditUndo' />"
1500         "    <toolitem action='EditRedo' />"
1501         "    <separator />"
1502         "    <toolitem action='EditCopy' />"
1503         "    <toolitem action='EditCut' />"
1504         "    <toolitem action='EditPaste' />"
1505         "    <separator />"
1506         "    <toolitem action='ZoomSelection' />"
1507         "    <toolitem action='ZoomDrawing' />"
1508         "    <toolitem action='ZoomPage' />"
1509         "    <separator />"
1510         "    <toolitem action='EditDuplicate' />"
1511         "    <toolitem action='EditClone' />"
1512         "    <toolitem action='EditUnlinkClone' />"
1513         "    <separator />"
1514         "    <toolitem action='SelectionGroup' />"
1515         "    <toolitem action='SelectionUnGroup' />"
1516         "    <separator />"
1517         "    <toolitem action='DialogFillStroke' />"
1518         "    <toolitem action='DialogText' />"
1519         "    <toolitem action='DialogXMLEditor' />"
1520         "    <toolitem action='DialogAlignDistribute' />"
1521         "    <separator />"
1522         "    <toolitem action='DialogPreferences' />"
1523         "    <toolitem action='DialogDocumentProperties' />"
1524         "  </toolbar>"
1525         "</ui>";
1526     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1529     GtkUIManager* mgr = gtk_ui_manager_new();
1530     GError* errVal = 0;
1532     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1533     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1535     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1536     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1537         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1538     }
1539     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1540     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1541     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1544     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1547 static void
1548 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1552 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1554     gtk_widget_show(toolbox_toplevel);
1555     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1557     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1558     if (!shown_toolbox) {
1559         return;
1560     }
1561     gtk_widget_show(toolbox);
1563     gtk_widget_show_all(shown_toolbox);
1566 void
1567 aux_toolbox_space(GtkWidget *tb, gint space)
1569     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1572 static GtkWidget *
1573 sp_empty_toolbox_new(SPDesktop *desktop)
1575     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1576     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1577     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1579     gtk_widget_show_all(tbl);
1580     sp_set_font_size_smaller (tbl);
1582     return tbl;
1585 // helper UI functions
1587 GtkWidget *
1588 sp_tb_spinbutton(
1589     gchar *label, gchar const *tooltip,
1590     gchar const *path, gchar const *data, gdouble def,
1591     GtkWidget *us,
1592     GtkWidget *tbl,
1593     gboolean altx, gchar const *altx_mark,
1594     gdouble lower, gdouble upper, gdouble step, gdouble page,
1595     void (*callback)(GtkAdjustment *, GtkWidget *),
1596     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1598     GtkTooltips *tt = gtk_tooltips_new();
1600     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1602     GtkWidget *l = gtk_label_new(label);
1603     gtk_widget_show(l);
1604     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1605     gtk_container_add(GTK_CONTAINER(hb), l);
1607     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1608                                       lower, upper, step, page, page);
1609     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1610     if (us)
1611         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1613     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1614     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1615     if (altx)
1616         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1617     gtk_widget_set_size_request(sb,
1618                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1619                                 AUX_SPINBUTTON_HEIGHT);
1620     gtk_widget_show(sb);
1621     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1622     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1623     gtk_container_add(GTK_CONTAINER(hb), sb);
1624     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1626     return hb;
1629 #define MODE_LABEL_WIDTH 70
1631 //########################
1632 //##       Star         ##
1633 //########################
1635 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1637     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1639     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1640         // do not remember prefs if this call is initiated by an undo change, because undoing object
1641         // creation sets bogus values to its attributes before it is deleted
1642         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1643     }
1645     // quit if run by the attr_changed listener
1646     if (g_object_get_data( dataKludge, "freeze" )) {
1647         return;
1648     }
1650     // in turn, prevent listener from responding
1651     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1653     bool modmade = false;
1655     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1656     GSList const *items = selection->itemList();
1657     for (; items != NULL; items = items->next) {
1658         if (SP_IS_STAR((SPItem *) items->data)) {
1659             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1660             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1661             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1662                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1663                                     + M_PI / (gint)adj->value));
1664             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1665             modmade = true;
1666         }
1667     }
1668     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1669                                    _("Star: Change number of corners"));
1671     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1674 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1676     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1678     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1679         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1680     }
1682     // quit if run by the attr_changed listener
1683     if (g_object_get_data( dataKludge, "freeze" )) {
1684         return;
1685     }
1687     // in turn, prevent listener from responding
1688     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1690     bool modmade = false;
1691     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1692     GSList const *items = selection->itemList();
1693     for (; items != NULL; items = items->next) {
1694         if (SP_IS_STAR((SPItem *) items->data)) {
1695             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1697             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1698             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1699             if (r2 < r1) {
1700                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1701             } else {
1702                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1703             }
1705             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1706             modmade = true;
1707         }
1708     }
1710     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1711                                    _("Star: Change spoke ratio"));
1713     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1716 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1718     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1719     bool flat = ege_select_one_action_get_active( act ) == 0;
1721     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1722         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1723                                     flat ? "true" : "false" );
1724     }
1726     // quit if run by the attr_changed listener
1727     if (g_object_get_data( dataKludge, "freeze" )) {
1728         return;
1729     }
1731     // in turn, prevent listener from responding
1732     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1734     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1735     GSList const *items = selection->itemList();
1736     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1737     bool modmade = false;
1739     if ( prop_action ) {
1740         gtk_action_set_sensitive( prop_action, !flat );
1741     }
1743     for (; items != NULL; items = items->next) {
1744         if (SP_IS_STAR((SPItem *) items->data)) {
1745             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1746             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1747             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1748             modmade = true;
1749         }
1750     }
1752     if (modmade) {
1753         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1754                          flat ? _("Make polygon") : _("Make star"));
1755     }
1757     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1760 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1762     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1764     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1765         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1766     }
1768     // quit if run by the attr_changed listener
1769     if (g_object_get_data( dataKludge, "freeze" )) {
1770         return;
1771     }
1773     // in turn, prevent listener from responding
1774     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1776     bool modmade = false;
1778     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1779     GSList const *items = selection->itemList();
1780     for (; items != NULL; items = items->next) {
1781         if (SP_IS_STAR((SPItem *) items->data)) {
1782             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1783             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1784             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1785             modmade = true;
1786         }
1787     }
1788     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1789                                    _("Star: Change rounding"));
1791     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1794 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1796     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1798     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1799         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1800     }
1802     // quit if run by the attr_changed listener
1803     if (g_object_get_data( dataKludge, "freeze" )) {
1804         return;
1805     }
1807     // in turn, prevent listener from responding
1808     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1810     bool modmade = false;
1812     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1813     GSList const *items = selection->itemList();
1814     for (; items != NULL; items = items->next) {
1815         if (SP_IS_STAR((SPItem *) items->data)) {
1816             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1817             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1818             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1819             modmade = true;
1820         }
1821     }
1822     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1823                                    _("Star: Change randomization"));
1825     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1829 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1830                                        gchar const */*old_value*/, gchar const */*new_value*/,
1831                                        bool /*is_interactive*/, gpointer data)
1833     GtkWidget *tbl = GTK_WIDGET(data);
1835     // quit if run by the _changed callbacks
1836     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1837         return;
1838     }
1840     // in turn, prevent callbacks from responding
1841     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1843     GtkAdjustment *adj = 0;
1845     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1846     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1848     if (!strcmp(name, "inkscape:randomized")) {
1849         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1850         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1851     } else if (!strcmp(name, "inkscape:rounded")) {
1852         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1853         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1854     } else if (!strcmp(name, "inkscape:flatsided")) {
1855         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1856         char const *flatsides = repr->attribute("inkscape:flatsided");
1857         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1858         if ( flatsides && !strcmp(flatsides,"false") ) {
1859             ege_select_one_action_set_active( flat_action, 1 );
1860             gtk_action_set_sensitive( prop_action, TRUE );
1861         } else {
1862             ege_select_one_action_set_active( flat_action, 0 );
1863             gtk_action_set_sensitive( prop_action, FALSE );
1864         }
1865     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1866         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1867         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1868         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1869         if (r2 < r1) {
1870             gtk_adjustment_set_value(adj, r2/r1);
1871         } else {
1872             gtk_adjustment_set_value(adj, r1/r2);
1873         }
1874     } else if (!strcmp(name, "sodipodi:sides")) {
1875         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1876         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1877     }
1879     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1883 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1885     NULL, /* child_added */
1886     NULL, /* child_removed */
1887     star_tb_event_attr_changed,
1888     NULL, /* content_changed */
1889     NULL  /* order_changed */
1890 };
1893 /**
1894  *  \param selection Should not be NULL.
1895  */
1896 static void
1897 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1899     int n_selected = 0;
1900     Inkscape::XML::Node *repr = NULL;
1902     purge_repr_listener( tbl, tbl );
1904     for (GSList const *items = selection->itemList();
1905          items != NULL;
1906          items = items->next)
1907     {
1908         if (SP_IS_STAR((SPItem *) items->data)) {
1909             n_selected++;
1910             repr = SP_OBJECT_REPR((SPItem *) items->data);
1911         }
1912     }
1914     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1916     if (n_selected == 0) {
1917         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1918     } else if (n_selected == 1) {
1919         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1921         if (repr) {
1922             g_object_set_data( tbl, "repr", repr );
1923             Inkscape::GC::anchor(repr);
1924             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1925             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1926         }
1927     } else {
1928         // FIXME: implement averaging of all parameters for multiple selected stars
1929         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1930         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1931     }
1935 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1937     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1938     // callbacks to lump all the changes for all selected objects in one undo step
1940     GtkAdjustment *adj = 0;
1942     // fixme: make settable in prefs!
1943     gint mag = 5;
1944     gdouble prop = 0.5;
1945     gboolean flat = FALSE;
1946     gdouble randomized = 0;
1947     gdouble rounded = 0;
1949     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1950     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1952     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1953     gtk_action_set_sensitive( sb2, !flat );
1955     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1956     gtk_adjustment_set_value(adj, mag);
1957     gtk_adjustment_value_changed(adj);
1959     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1960     gtk_adjustment_set_value(adj, prop);
1961     gtk_adjustment_value_changed(adj);
1963     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1964     gtk_adjustment_set_value(adj, rounded);
1965     gtk_adjustment_value_changed(adj);
1967     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1968     gtk_adjustment_set_value(adj, randomized);
1969     gtk_adjustment_value_changed(adj);
1973 void
1974 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1976     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1977     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1978     GtkWidget *l = gtk_label_new(NULL);
1979     gtk_label_set_markup(GTK_LABEL(l), title);
1980     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1981     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1982     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1986 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1988     {
1989         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1990         ege_output_action_set_use_markup( act, TRUE );
1991         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1992         g_object_set_data( holder, "mode_action", act );
1993     }
1995     {
1996         EgeAdjustmentAction* eact = 0;
1997         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1998         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2000         /* Flatsided checkbox */
2001         {
2002             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2004             GtkTreeIter iter;
2005             gtk_list_store_append( model, &iter );
2006             gtk_list_store_set( model, &iter,
2007                                 0, _("Polygon"),
2008                                 1, _("Regular polygon (with one handle) instead of a star"),
2009                                 2, "star_flat",
2010                                 -1 );
2012             gtk_list_store_append( model, &iter );
2013             gtk_list_store_set( model, &iter,
2014                                 0, _("Star"),
2015                                 1, _("Star instead of a regular polygon (with one handle)"),
2016                                 2, "star_angled",
2017                                 -1 );
2019             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2020             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2021             g_object_set_data( holder, "flat_action", act );
2023             ege_select_one_action_set_appearance( act, "full" );
2024             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2025             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2026             ege_select_one_action_set_icon_column( act, 2 );
2027             ege_select_one_action_set_tooltip_column( act, 1  );
2029             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2030             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2031         }
2033         /* Magnitude */
2034         {
2035         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2036         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2037         eact = create_adjustment_action( "MagnitudeAction",
2038                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2039                                          "tools.shapes.star", "magnitude", 3,
2040                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2041                                          3, 1024, 1, 5,
2042                                          labels, values, G_N_ELEMENTS(labels),
2043                                          sp_stb_magnitude_value_changed,
2044                                          1.0, 0 );
2045         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2046         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2047         }
2049         /* Spoke ratio */
2050         {
2051         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2052         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2053         eact = create_adjustment_action( "SpokeAction",
2054                                          _("Spoke ratio"), _("Spoke ratio:"),
2055                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2056                                          // Base radius is the same for the closest handle.
2057                                          _("Base radius to tip radius ratio"),
2058                                          "tools.shapes.star", "proportion", 0.5,
2059                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2060                                          0.01, 1.0, 0.01, 0.1,
2061                                          labels, values, G_N_ELEMENTS(labels),
2062                                          sp_stb_proportion_value_changed );
2063         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2064         g_object_set_data( holder, "prop_action", eact );
2065         }
2067         if ( !isFlatSided ) {
2068             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2069         } else {
2070             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2071         }
2073         /* Roundedness */
2074         {
2075         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2076         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2077         eact = create_adjustment_action( "RoundednessAction",
2078                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2079                                          "tools.shapes.star", "rounded", 0.0,
2080                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2081                                          -10.0, 10.0, 0.01, 0.1,
2082                                          labels, values, G_N_ELEMENTS(labels),
2083                                          sp_stb_rounded_value_changed );
2084         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2085         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2086         }
2088         /* Randomization */
2089         {
2090         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2091         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2092         eact = create_adjustment_action( "RandomizationAction",
2093                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2094                                          "tools.shapes.star", "randomized", 0.0,
2095                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2096                                          -10.0, 10.0, 0.001, 0.01,
2097                                          labels, values, G_N_ELEMENTS(labels),
2098                                          sp_stb_randomized_value_changed, 0.1, 3 );
2099         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2100         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2101         }
2102     }
2104     {
2105         /* Reset */
2106         {
2107             GtkAction* act = gtk_action_new( "StarResetAction",
2108                                              _("Defaults"),
2109                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2110                                              GTK_STOCK_CLEAR );
2111             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2112             gtk_action_group_add_action( mainActions, act );
2113             gtk_action_set_sensitive( act, TRUE );
2114         }
2115     }
2117     sigc::connection *connection = new sigc::connection(
2118         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2119         );
2120     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2121     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2125 //########################
2126 //##       Rect         ##
2127 //########################
2129 static void sp_rtb_sensitivize( GObject *tbl )
2131     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2132     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2133     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2135     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2136         gtk_action_set_sensitive( not_rounded, FALSE );
2137     } else {
2138         gtk_action_set_sensitive( not_rounded, TRUE );
2139     }
2143 static void
2144 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2145                           void (*setter)(SPRect *, gdouble))
2147     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2149     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2150     SPUnit const *unit = tracker->getActiveUnit();
2152     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2153         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2154     }
2156     // quit if run by the attr_changed listener
2157     if (g_object_get_data( tbl, "freeze" )) {
2158         return;
2159     }
2161     // in turn, prevent listener from responding
2162     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2164     bool modmade = false;
2165     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2166     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2167         if (SP_IS_RECT(items->data)) {
2168             if (adj->value != 0) {
2169                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2170             } else {
2171                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2172             }
2173             modmade = true;
2174         }
2175     }
2177     sp_rtb_sensitivize( tbl );
2179     if (modmade) {
2180         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2181                                    _("Change rectangle"));
2182     }
2184     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2187 static void
2188 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2190     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2193 static void
2194 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2196     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2199 static void
2200 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2202     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2205 static void
2206 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2208     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2213 static void
2214 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2216     GtkAdjustment *adj = 0;
2218     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2219     gtk_adjustment_set_value(adj, 0.0);
2220     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2221     gtk_adjustment_value_changed(adj);
2223     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2224     gtk_adjustment_set_value(adj, 0.0);
2225     gtk_adjustment_value_changed(adj);
2227     sp_rtb_sensitivize( obj );
2230 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2231                                        gchar const */*old_value*/, gchar const */*new_value*/,
2232                                        bool /*is_interactive*/, gpointer data)
2234     GObject *tbl = G_OBJECT(data);
2236     // quit if run by the _changed callbacks
2237     if (g_object_get_data( tbl, "freeze" )) {
2238         return;
2239     }
2241     // in turn, prevent callbacks from responding
2242     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2244     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2245     SPUnit const *unit = tracker->getActiveUnit();
2247     gpointer item = g_object_get_data( tbl, "item" );
2248     if (item && SP_IS_RECT(item)) {
2249         {
2250             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2251             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2252             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2253         }
2255         {
2256             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2257             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2258             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2259         }
2261         {
2262             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2263             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2264             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2265         }
2267         {
2268             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2269             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2270             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2271         }
2272     }
2274     sp_rtb_sensitivize( tbl );
2276     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2280 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2281     NULL, /* child_added */
2282     NULL, /* child_removed */
2283     rect_tb_event_attr_changed,
2284     NULL, /* content_changed */
2285     NULL  /* order_changed */
2286 };
2288 /**
2289  *  \param selection should not be NULL.
2290  */
2291 static void
2292 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2294     int n_selected = 0;
2295     Inkscape::XML::Node *repr = NULL;
2296     SPItem *item = NULL;
2298     if ( g_object_get_data( tbl, "repr" ) ) {
2299         g_object_set_data( tbl, "item", NULL );
2300     }
2301     purge_repr_listener( tbl, tbl );
2303     for (GSList const *items = selection->itemList();
2304          items != NULL;
2305          items = items->next) {
2306         if (SP_IS_RECT((SPItem *) items->data)) {
2307             n_selected++;
2308             item = (SPItem *) items->data;
2309             repr = SP_OBJECT_REPR(item);
2310         }
2311     }
2313     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2315     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2317     if (n_selected == 0) {
2318         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2320         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2321         gtk_action_set_sensitive(w, FALSE);
2322         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2323         gtk_action_set_sensitive(h, FALSE);
2325     } else if (n_selected == 1) {
2326         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2327         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2329         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2330         gtk_action_set_sensitive(w, TRUE);
2331         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2332         gtk_action_set_sensitive(h, TRUE);
2334         if (repr) {
2335             g_object_set_data( tbl, "repr", repr );
2336             g_object_set_data( tbl, "item", item );
2337             Inkscape::GC::anchor(repr);
2338             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2339             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2340         }
2341     } else {
2342         // FIXME: implement averaging of all parameters for multiple selected
2343         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2344         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2345         sp_rtb_sensitivize( tbl );
2346     }
2350 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2352     EgeAdjustmentAction* eact = 0;
2354     {
2355         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2356         ege_output_action_set_use_markup( act, TRUE );
2357         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2358         g_object_set_data( holder, "mode_action", act );
2359     }
2361     // rx/ry units menu: create
2362     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2363     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2364     // fixme: add % meaning per cent of the width/height
2365     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2366     g_object_set_data( holder, "tracker", tracker );
2368     /* W */
2369     {
2370         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2371         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2372         eact = create_adjustment_action( "RectWidthAction",
2373                                          _("Width"), _("W:"), _("Width of rectangle"),
2374                                          "tools.shapes.rect", "width", 0,
2375                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2376                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2377                                          labels, values, G_N_ELEMENTS(labels),
2378                                          sp_rtb_width_value_changed );
2379         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2380         g_object_set_data( holder, "width_action", eact );
2381         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2382         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2383     }
2385     /* H */
2386     {
2387         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2388         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2389         eact = create_adjustment_action( "RectHeightAction",
2390                                          _("Height"), _("H:"), _("Height of rectangle"),
2391                                          "tools.shapes.rect", "height", 0,
2392                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2393                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2394                                          labels, values, G_N_ELEMENTS(labels),
2395                                          sp_rtb_height_value_changed );
2396         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2397         g_object_set_data( holder, "height_action", eact );
2398         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2399         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2400     }
2402     /* rx */
2403     {
2404         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2405         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2406         eact = create_adjustment_action( "RadiusXAction",
2407                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2408                                          "tools.shapes.rect", "rx", 0,
2409                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2410                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2411                                          labels, values, G_N_ELEMENTS(labels),
2412                                          sp_rtb_rx_value_changed);
2413         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2414         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2415     }
2417     /* ry */
2418     {
2419         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2420         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2421         eact = create_adjustment_action( "RadiusYAction",
2422                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2423                                          "tools.shapes.rect", "ry", 0,
2424                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2425                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2426                                          labels, values, G_N_ELEMENTS(labels),
2427                                          sp_rtb_ry_value_changed);
2428         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2429         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2430     }
2432     // add the units menu
2433     {
2434         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2435         gtk_action_group_add_action( mainActions, act );
2436     }
2438     /* Reset */
2439     {
2440         InkAction* inky = ink_action_new( "RectResetAction",
2441                                           _("Not rounded"),
2442                                           _("Make corners sharp"),
2443                                           "squared_corner",
2444                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2445         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2446         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2447         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2448         g_object_set_data( holder, "not_rounded", inky );
2449     }
2451     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2452     sp_rtb_sensitivize( holder );
2454     sigc::connection *connection = new sigc::connection(
2455         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2456         );
2457     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2458     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2461 //########################
2462 //##       3D Box       ##
2463 //########################
2465 // normalize angle so that it lies in the interval [0,360]
2466 static double box3d_normalize_angle (double a) {
2467     double angle = a + ((int) (a/360.0))*360;
2468     if (angle < 0) {
2469         angle += 360.0;
2470     }
2471     return angle;
2474 static void
2475 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2476                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2477     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2478     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2479     //       are reset).
2480     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2482     if (is_infinite) {
2483         gtk_toggle_action_set_active(tact, TRUE);
2484         gtk_action_set_sensitive(act, TRUE);
2486         double angle = persp3d_get_infinite_angle(persp, axis);
2487         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2488             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2489         }
2490     } else {
2491         gtk_toggle_action_set_active(tact, FALSE);
2492         gtk_action_set_sensitive(act, FALSE);
2493     }
2496 static void
2497 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2498     if (!persp_repr) {
2499         g_print ("No perspective given to box3d_resync_toolbar().\n");
2500         return;
2501     }
2503     GtkWidget *tbl = GTK_WIDGET(data);
2504     GtkAdjustment *adj = 0;
2505     GtkAction *act = 0;
2506     GtkToggleAction *tact = 0;
2507     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2508     {
2509         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2510         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2511         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2513         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2514     }
2515     {
2516         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2517         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2518         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2520         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2521     }
2522     {
2523         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2524         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2525         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2527         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2528     }
2531 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2532                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2533                                                   bool /*is_interactive*/, gpointer data)
2535     GtkWidget *tbl = GTK_WIDGET(data);
2537     // quit if run by the attr_changed listener
2538     // note: it used to work without the differently called freeze_ attributes (here and in
2539     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2540     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2541         return;
2542     }
2544     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2545     // sp_document_maybe_done() when the document is undo insensitive)
2546     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2548     // TODO: Only update the appropriate part of the toolbar
2549 //    if (!strcmp(name, "inkscape:vp_z")) {
2550         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2551 //    }
2553     Persp3D *persp = persp3d_get_from_repr(repr);
2554     persp3d_update_box_reprs(persp);
2556     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2559 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2561     NULL, /* child_added */
2562     NULL, /* child_removed */
2563     box3d_persp_tb_event_attr_changed,
2564     NULL, /* content_changed */
2565     NULL  /* order_changed */
2566 };
2568 /**
2569  *  \param selection Should not be NULL.
2570  */
2571 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2572 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2573 static void
2574 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2576     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2577     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2578     // update the perspectives with infinite VPs and leave the other ones untouched).
2580     Inkscape::XML::Node *persp_repr = NULL;
2581     purge_repr_listener(tbl, tbl);
2583     SPItem *item = selection->singleItem();
2584     if (item && SP_IS_BOX3D(item)) {
2585         // FIXME: Also deal with multiple selected boxes
2586         SPBox3D *box = SP_BOX3D(item);
2587         Persp3D *persp = box3d_get_perspective(box);
2588         persp_repr = SP_OBJECT_REPR(persp);
2589         if (persp_repr) {
2590             g_object_set_data(tbl, "repr", persp_repr);
2591             Inkscape::GC::anchor(persp_repr);
2592             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2593             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2594         }
2596         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2597         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2599         box3d_resync_toolbar(persp_repr, tbl);
2600     }
2603 static void
2604 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2606     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2607     SPDocument *document = sp_desktop_document(desktop);
2609     // quit if run by the attr_changed listener
2610     // note: it used to work without the differently called freeze_ attributes (here and in
2611     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2612     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2613         return;
2614     }
2616     // in turn, prevent listener from responding
2617     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2619     //Persp3D *persp = document->current_persp3d;
2620     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2621     if (sel_persps.empty()) {
2622         // this can happen when the document is created; we silently ignore it
2623         return;
2624     }
2625     Persp3D *persp = sel_persps.front();
2627     persp->tmat.set_infinite_direction (axis, adj->value);
2628     SP_OBJECT(persp)->updateRepr();
2630     // TODO: use the correct axis here, too
2631     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2633     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2637 static void
2638 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2640     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2643 static void
2644 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2646     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2649 static void
2650 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2652     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2656 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2658     // TODO: Take all selected perspectives into account
2659     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2660     if (sel_persps.empty()) {
2661         // this can happen when the document is created; we silently ignore it
2662         return;
2663     }
2664     Persp3D *persp = sel_persps.front();
2666     bool set_infinite = gtk_toggle_action_get_active(act);
2667     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2670 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2672     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2675 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2677     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2680 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2682     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2685 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2687     EgeAdjustmentAction* eact = 0;
2688     SPDocument *document = sp_desktop_document (desktop);
2689     Persp3D *persp = document->current_persp3d;
2691     EgeAdjustmentAction* box3d_angle_x = 0;
2692     EgeAdjustmentAction* box3d_angle_y = 0;
2693     EgeAdjustmentAction* box3d_angle_z = 0;
2695     /* Angle X */
2696     {
2697         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2698         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2699         eact = create_adjustment_action( "3DBoxAngleXAction",
2700                                          _("Angle in X direction"), _("Angle X:"),
2701                                          // Translators: PL is short for 'perspective line'
2702                                          _("Angle of PLs in X direction"),
2703                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2704                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2705                                          -360.0, 360.0, 1.0, 10.0,
2706                                          labels, values, G_N_ELEMENTS(labels),
2707                                          box3d_angle_x_value_changed );
2708         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2709         g_object_set_data( holder, "box3d_angle_x_action", eact );
2710         box3d_angle_x = eact;
2711     }
2713     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2714         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2715     } else {
2716         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2717     }
2720     /* VP X state */
2721     {
2722         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2723                                                       // Translators: VP is short for 'vanishing point'
2724                                                       _("State of VP in X direction"),
2725                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2726                                                       "toggle_vp_x",
2727                                                       Inkscape::ICON_SIZE_DECORATION );
2728         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2729         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2730         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2731         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2732         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2733     }
2735     /* Angle Y */
2736     {
2737         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2738         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2739         eact = create_adjustment_action( "3DBoxAngleYAction",
2740                                          _("Angle in Y direction"), _("Angle Y:"),
2741                                          // Translators: PL is short for 'perspective line'
2742                                          _("Angle of PLs in Y direction"),
2743                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2744                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2745                                          -360.0, 360.0, 1.0, 10.0,
2746                                          labels, values, G_N_ELEMENTS(labels),
2747                                          box3d_angle_y_value_changed );
2748         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2749         g_object_set_data( holder, "box3d_angle_y_action", eact );
2750         box3d_angle_y = eact;
2751     }
2753     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2754         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2755     } else {
2756         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2757     }
2759     /* VP Y state */
2760     {
2761         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2762                                                       // Translators: VP is short for 'vanishing point'
2763                                                       _("State of VP in Y direction"),
2764                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2765                                                       "toggle_vp_y",
2766                                                       Inkscape::ICON_SIZE_DECORATION );
2767         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2768         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2769         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2770         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2771         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2772     }
2774     /* Angle Z */
2775     {
2776         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2777         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2778         eact = create_adjustment_action( "3DBoxAngleZAction",
2779                                          _("Angle in Z direction"), _("Angle Z:"),
2780                                          // Translators: PL is short for 'perspective line'
2781                                          _("Angle of PLs in Z direction"),
2782                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2783                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2784                                          -360.0, 360.0, 1.0, 10.0,
2785                                          labels, values, G_N_ELEMENTS(labels),
2786                                          box3d_angle_z_value_changed );
2787         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2788         g_object_set_data( holder, "box3d_angle_z_action", eact );
2789         box3d_angle_z = eact;
2790     }
2792     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2793         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2794     } else {
2795         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2796     }
2798     /* VP Z state */
2799     {
2800         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2801                                                       // Translators: VP is short for 'vanishing point'
2802                                                       _("State of VP in Z direction"),
2803                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2804                                                       "toggle_vp_z",
2805                                                       Inkscape::ICON_SIZE_DECORATION );
2806         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2807         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2808         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2809         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2810         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2811     }
2813     sigc::connection *connection = new sigc::connection(
2814         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2815        );
2816     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2817     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2820 //########################
2821 //##       Spiral       ##
2822 //########################
2824 static void
2825 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2827     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2829     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2830         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2831     }
2833     // quit if run by the attr_changed listener
2834     if (g_object_get_data( tbl, "freeze" )) {
2835         return;
2836     }
2838     // in turn, prevent listener from responding
2839     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2841     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2843     bool modmade = false;
2844     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2845          items != NULL;
2846          items = items->next)
2847     {
2848         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2849             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2850             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2851             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2852             modmade = true;
2853         }
2854     }
2856     g_free(namespaced_name);
2858     if (modmade) {
2859         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2860                                    _("Change spiral"));
2861     }
2863     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2866 static void
2867 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2869     sp_spl_tb_value_changed(adj, tbl, "revolution");
2872 static void
2873 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2875     sp_spl_tb_value_changed(adj, tbl, "expansion");
2878 static void
2879 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2881     sp_spl_tb_value_changed(adj, tbl, "t0");
2884 static void
2885 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2887     GtkWidget *tbl = GTK_WIDGET(obj);
2889     GtkAdjustment *adj;
2891     // fixme: make settable
2892     gdouble rev = 5;
2893     gdouble exp = 1.0;
2894     gdouble t0 = 0.0;
2896     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2897     gtk_adjustment_set_value(adj, rev);
2898     gtk_adjustment_value_changed(adj);
2900     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2901     gtk_adjustment_set_value(adj, exp);
2902     gtk_adjustment_value_changed(adj);
2904     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2905     gtk_adjustment_set_value(adj, t0);
2906     gtk_adjustment_value_changed(adj);
2908     spinbutton_defocus(GTK_OBJECT(tbl));
2912 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2913                                          gchar const */*old_value*/, gchar const */*new_value*/,
2914                                          bool /*is_interactive*/, gpointer data)
2916     GtkWidget *tbl = GTK_WIDGET(data);
2918     // quit if run by the _changed callbacks
2919     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2920         return;
2921     }
2923     // in turn, prevent callbacks from responding
2924     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2926     GtkAdjustment *adj;
2927     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2928     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2930     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2931     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2933     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2934     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2936     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2940 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2941     NULL, /* child_added */
2942     NULL, /* child_removed */
2943     spiral_tb_event_attr_changed,
2944     NULL, /* content_changed */
2945     NULL  /* order_changed */
2946 };
2948 static void
2949 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2951     int n_selected = 0;
2952     Inkscape::XML::Node *repr = NULL;
2954     purge_repr_listener( tbl, tbl );
2956     for (GSList const *items = selection->itemList();
2957          items != NULL;
2958          items = items->next)
2959     {
2960         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2961             n_selected++;
2962             repr = SP_OBJECT_REPR((SPItem *) items->data);
2963         }
2964     }
2966     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2968     if (n_selected == 0) {
2969         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2970     } else if (n_selected == 1) {
2971         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2973         if (repr) {
2974             g_object_set_data( tbl, "repr", repr );
2975             Inkscape::GC::anchor(repr);
2976             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2977             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2978         }
2979     } else {
2980         // FIXME: implement averaging of all parameters for multiple selected
2981         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2982         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2983     }
2987 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2989     EgeAdjustmentAction* eact = 0;
2991     {
2992         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2993         ege_output_action_set_use_markup( act, TRUE );
2994         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2995         g_object_set_data( holder, "mode_action", act );
2996     }
2998     /* Revolution */
2999     {
3000         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3001         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3002         eact = create_adjustment_action( "SpiralRevolutionAction",
3003                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3004                                          "tools.shapes.spiral", "revolution", 3.0,
3005                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3006                                          0.01, 1024.0, 0.1, 1.0,
3007                                          labels, values, G_N_ELEMENTS(labels),
3008                                          sp_spl_tb_revolution_value_changed, 1, 2);
3009         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3010     }
3012     /* Expansion */
3013     {
3014         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3015         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3016         eact = create_adjustment_action( "SpiralExpansionAction",
3017                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3018                                          "tools.shapes.spiral", "expansion", 1.0,
3019                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3020                                          0.0, 1000.0, 0.01, 1.0,
3021                                          labels, values, G_N_ELEMENTS(labels),
3022                                          sp_spl_tb_expansion_value_changed);
3023         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3024     }
3026     /* T0 */
3027     {
3028         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3029         gdouble values[] = {0, 0.5, 0.9};
3030         eact = create_adjustment_action( "SpiralT0Action",
3031                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3032                                          "tools.shapes.spiral", "t0", 0.0,
3033                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3034                                          0.0, 0.999, 0.01, 1.0,
3035                                          labels, values, G_N_ELEMENTS(labels),
3036                                          sp_spl_tb_t0_value_changed);
3037         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3038     }
3040     /* Reset */
3041     {
3042         InkAction* inky = ink_action_new( "SpiralResetAction",
3043                                           _("Defaults"),
3044                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3045                                           GTK_STOCK_CLEAR,
3046                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3047         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3048         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3049     }
3052     sigc::connection *connection = new sigc::connection(
3053         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3054         );
3055     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3056     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3059 //########################
3060 //##     Pen/Pencil    ##
3061 //########################
3064 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3066     // Put stuff here
3069 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3071     // Put stuff here
3074 //########################
3075 //##       Tweak        ##
3076 //########################
3078 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3080     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3083 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3085     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3088 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3090     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3093 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3095     int mode = ege_select_one_action_get_active( act );
3096     prefs_set_int_attribute("tools.tweak", "mode", mode);
3098     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3099     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3100     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3101     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3102     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3103     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3104     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3105         if (doh) gtk_action_set_sensitive (doh, TRUE);
3106         if (dos) gtk_action_set_sensitive (dos, TRUE);
3107         if (dol) gtk_action_set_sensitive (dol, TRUE);
3108         if (doo) gtk_action_set_sensitive (doo, TRUE);
3109         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3110         if (fid) gtk_action_set_sensitive (fid, FALSE);
3111     } else {
3112         if (doh) gtk_action_set_sensitive (doh, FALSE);
3113         if (dos) gtk_action_set_sensitive (dos, FALSE);
3114         if (dol) gtk_action_set_sensitive (dol, FALSE);
3115         if (doo) gtk_action_set_sensitive (doo, FALSE);
3116         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3117         if (fid) gtk_action_set_sensitive (fid, TRUE);
3118     }
3121 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3123     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3126 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3127     bool show = gtk_toggle_action_get_active( act );
3128     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3130 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3131     bool show = gtk_toggle_action_get_active( act );
3132     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3134 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3135     bool show = gtk_toggle_action_get_active( act );
3136     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3138 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3139     bool show = gtk_toggle_action_get_active( act );
3140     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3143 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3145     {
3146         /* Width */
3147         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3148         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3149         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3150                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3151                                                               "tools.tweak", "width", 15,
3152                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3153                                                               1, 100, 1.0, 10.0,
3154                                                               labels, values, G_N_ELEMENTS(labels),
3155                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3156         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3157         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3158     }
3161     {
3162         /* Force */
3163         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3164         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3165         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3166                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3167                                                               "tools.tweak", "force", 20,
3168                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3169                                                               1, 100, 1.0, 10.0,
3170                                                               labels, values, G_N_ELEMENTS(labels),
3171                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3172         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3173         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3174     }
3176     /* Mode */
3177     {
3178         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3180         GtkTreeIter iter;
3181         gtk_list_store_append( model, &iter );
3182         gtk_list_store_set( model, &iter,
3183                             0, _("Push mode"),
3184                             1, _("Push parts of paths in any direction"),
3185                             2, "tweak_push_mode",
3186                             -1 );
3188         gtk_list_store_append( model, &iter );
3189         gtk_list_store_set( model, &iter,
3190                             0, _("Shrink mode"),
3191                             1, _("Shrink (inset) parts of paths"),
3192                             2, "tweak_shrink_mode",
3193                             -1 );
3195         gtk_list_store_append( model, &iter );
3196         gtk_list_store_set( model, &iter,
3197                             0, _("Grow mode"),
3198                             1, _("Grow (outset) parts of paths"),
3199                             2, "tweak_grow_mode",
3200                             -1 );
3202         gtk_list_store_append( model, &iter );
3203         gtk_list_store_set( model, &iter,
3204                             0, _("Attract mode"),
3205                             1, _("Attract parts of paths towards cursor"),
3206                             2, "tweak_attract_mode",
3207                             -1 );
3209         gtk_list_store_append( model, &iter );
3210         gtk_list_store_set( model, &iter,
3211                             0, _("Repel mode"),
3212                             1, _("Repel parts of paths from cursor"),
3213                             2, "tweak_repel_mode",
3214                             -1 );
3216         gtk_list_store_append( model, &iter );
3217         gtk_list_store_set( model, &iter,
3218                             0, _("Roughen mode"),
3219                             1, _("Roughen parts of paths"),
3220                             2, "tweak_roughen_mode",
3221                             -1 );
3223         gtk_list_store_append( model, &iter );
3224         gtk_list_store_set( model, &iter,
3225                             0, _("Color paint mode"),
3226                             1, _("Paint the tool's color upon selected objects"),
3227                             2, "tweak_colorpaint_mode",
3228                             -1 );
3230         gtk_list_store_append( model, &iter );
3231         gtk_list_store_set( model, &iter,
3232                             0, _("Color jitter mode"),
3233                             1, _("Jitter the colors of selected objects"),
3234                             2, "tweak_colorjitter_mode",
3235                             -1 );
3237         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3238         g_object_set( act, "short_label", _("Mode:"), NULL );
3239         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3240         g_object_set_data( holder, "mode_action", act );
3242         ege_select_one_action_set_appearance( act, "full" );
3243         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3244         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3245         ege_select_one_action_set_icon_column( act, 2 );
3246         ege_select_one_action_set_tooltip_column( act, 1  );
3248         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3249         ege_select_one_action_set_active( act, mode );
3250         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3252         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3253     }
3255     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3257     {
3258         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3259         ege_output_action_set_use_markup( act, TRUE );
3260         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3261         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3262             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3263         g_object_set_data( holder, "tweak_channels_label", act);
3264     }
3266     {
3267         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3268                                                       _("Hue"),
3269                                                       _("In color mode, act on objects' hue"),
3270                                                       NULL,
3271                                                       Inkscape::ICON_SIZE_DECORATION );
3272         g_object_set( act, "short_label", _("H"), NULL );
3273         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3274         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3275         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3276         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3277             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3278         g_object_set_data( holder, "tweak_doh", act);
3279     }
3280     {
3281         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3282                                                       _("Saturation"),
3283                                                       _("In color mode, act on objects' saturation"),
3284                                                       NULL,
3285                                                       Inkscape::ICON_SIZE_DECORATION );
3286         g_object_set( act, "short_label", _("S"), NULL );
3287         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3288         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3289         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3290         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3291             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3292         g_object_set_data( holder, "tweak_dos", act );
3293     }
3294     {
3295         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3296                                                       _("Lightness"),
3297                                                       _("In color mode, act on objects' lightness"),
3298                                                       NULL,
3299                                                       Inkscape::ICON_SIZE_DECORATION );
3300         g_object_set( act, "short_label", _("L"), NULL );
3301         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3302         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3303         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3304         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3305             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3306         g_object_set_data( holder, "tweak_dol", act );
3307     }
3308     {
3309         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3310                                                       _("Opacity"),
3311                                                       _("In color mode, act on objects' opacity"),
3312                                                       NULL,
3313                                                       Inkscape::ICON_SIZE_DECORATION );
3314         g_object_set( act, "short_label", _("O"), NULL );
3315         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3316         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3317         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3318         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3319             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3320         g_object_set_data( holder, "tweak_doo", act );
3321     }
3323     {   /* Fidelity */
3324         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3325         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3326         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3327                                                               _("Fidelity"), _("Fidelity:"),
3328                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3329                                                               "tools.tweak", "fidelity", 50,
3330                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3331                                                               1, 100, 1.0, 10.0,
3332                                                               labels, values, G_N_ELEMENTS(labels),
3333                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3334         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3335         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3336         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3337             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3338         g_object_set_data( holder, "tweak_fidelity", eact );
3339     }
3342     /* Use Pressure button */
3343     {
3344         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3345                                                       _("Pressure"),
3346                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3347                                                       "use_pressure",
3348                                                       Inkscape::ICON_SIZE_DECORATION );
3349         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3350         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3351         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3352     }
3357 //########################
3358 //##     Calligraphy    ##
3359 //########################
3360 static void update_presets_list(GObject *dataKludge ){
3361     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3362     if (sel) {
3363         ege_select_one_action_set_active(sel, 0 );
3364     }
3367 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3369     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3370     update_presets_list(tbl);
3373 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3375     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3376     update_presets_list(tbl);
3379 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3381     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3382     update_presets_list(tbl);
3385 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3387     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3388     update_presets_list(tbl);
3391 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3393     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3394     update_presets_list(tbl);
3397 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3399     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3400     update_presets_list(tbl);
3403 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3405     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3406     update_presets_list(tbl);
3409 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3411     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3412     update_presets_list(tbl);
3415 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3417     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3418     update_presets_list(tbl);
3421 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3423     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3424     update_presets_list(tbl);
3427 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3429     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3430     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3431     update_presets_list(tbl);
3432     if (calligraphy_angle )
3433         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3437 #define PROFILE_FLOAT_SIZE 7
3438 #define PROFILE_INT_SIZE 4
3439 struct ProfileFloatElement {
3440     char const *name;
3441     double def;
3442     double min;
3443     double max;
3444 };
3445 struct ProfileIntElement {
3446     char const *name;
3447     int def;
3448     int min;
3449     int max;
3450 };
3454 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3455     {"mass",0.02, 0.0, 1.0},
3456     {"wiggle",0.0, 0.0, 1.0},
3457     {"angle",30.0, -90.0, 90.0},
3458     {"thinning",0.1, -1.0, 1.0},
3459     {"tremor",0.0, 0.0, 1.0},
3460     {"flatness",0.9, 0.0, 1.0},
3461     {"cap_rounding",0.0, 0.0, 5.0}
3462 };
3463 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3464     {"width",15, 1, 100},
3465     {"usepressure",1,0,1},
3466     {"tracebackground",0,0,1},
3467     {"usetilt",1,0,1},
3468 };
3472 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3473     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3474     if (! desktop) return;
3476     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3477     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3478     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3480     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3481     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3482     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3483     
3484     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3485         ProfileFloatElement const &pe = f_profile[i];
3486         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3487         prefs_set_double_attribute(pref_path,pe.name,v);
3488     }    
3489     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3490         ProfileIntElement const &pe = i_profile[i];
3491         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3492         prefs_set_int_attribute(pref_path,pe.name,v);
3493     }
3494     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3496     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3497     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3498     GtkTreeIter iter;
3499     gtk_list_store_append( model, &iter );
3500     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3502     free(profile_id);
3503     free(pref_path);
3505     ege_select_one_action_set_active(selector, new_index);
3509 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3510     
3511     gint preset_index = ege_select_one_action_get_active( act ); 
3512     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3513        
3514     if ( profile_name) {        
3515         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3516         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3517             ProfileFloatElement const &pe = f_profile[i];
3518             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3519             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3520             if ( adj ) {
3521                 gtk_adjustment_set_value(adj, v);
3522             }
3523         } 
3524         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3525             ProfileIntElement const &pe = i_profile[i];
3526             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3527             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3528             if ( toggle ) {
3529                 gtk_toggle_action_set_active(toggle, v);
3530             } else printf("No toggle");
3531         }
3532         free(profile_name);
3533         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3534     }
3539 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3541     {
3542         EgeAdjustmentAction* calligraphy_angle = 0;
3544         {
3545         /* Width */
3546         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3547         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3548         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3549                                                               _("Pen Width"), _("Width:"),
3550                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3551                                                               "tools.calligraphic", "width", 15,
3552                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3553                                                               1, 100, 1.0, 10.0,
3554                                                               labels, values, G_N_ELEMENTS(labels),
3555                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3556         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3557         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3558         }
3560         {
3561         /* Thinning */
3562             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3563             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3564         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3565                                                               _("Stroke Thinning"), _("Thinning:"),
3566                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3567                                                               "tools.calligraphic", "thinning", 0.1,
3568                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3569                                                               -1.0, 1.0, 0.01, 0.1,
3570                                                               labels, values, G_N_ELEMENTS(labels),
3571                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3572         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3573         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3574         }
3576         {
3577         /* Angle */
3578         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3579         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3580         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3581                                                               _("Pen Angle"), _("Angle:"),
3582                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3583                                                               "tools.calligraphic", "angle", 30,
3584                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3585                                                               -90.0, 90.0, 1.0, 10.0,
3586                                                               labels, values, G_N_ELEMENTS(labels),
3587                                                               sp_ddc_angle_value_changed, 1, 0 );
3588         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3589         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3590         g_object_set_data( holder, "angle", eact );
3591         calligraphy_angle = eact;
3592         }
3594         {
3595         /* Fixation */
3596             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3597         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3598         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3599                                                               _("Fixation"), _("Fixation:"),
3600                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3601                                                               "tools.calligraphic", "flatness", 0.9,
3602                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3603                                                               0.0, 1.0, 0.01, 0.1,
3604                                                               labels, values, G_N_ELEMENTS(labels),
3605                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3606         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3607         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3608         }
3610         {
3611         /* Cap Rounding */
3612             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3613         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3614         // TRANSLATORS: "cap" means "end" (both start and finish) here
3615         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3616                                                               _("Cap rounding"), _("Caps:"),
3617                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3618                                                               "tools.calligraphic", "cap_rounding", 0.0,
3619                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3620                                                               0.0, 5.0, 0.01, 0.1,
3621                                                               labels, values, G_N_ELEMENTS(labels),
3622                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3623         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3624         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3625         }
3627         {
3628         /* Tremor */
3629             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3630         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3631         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3632                                                               _("Stroke Tremor"), _("Tremor:"),
3633                                                               _("Increase to make strokes rugged and trembling"),
3634                                                               "tools.calligraphic", "tremor", 0.0,
3635                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3636                                                               0.0, 1.0, 0.01, 0.1,
3637                                                               labels, values, G_N_ELEMENTS(labels),
3638                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3640         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3641         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3642         }
3644         {
3645         /* Wiggle */
3646         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3647         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3648         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3649                                                               _("Pen Wiggle"), _("Wiggle:"),
3650                                                               _("Increase to make the pen waver and wiggle"),
3651                                                               "tools.calligraphic", "wiggle", 0.0,
3652                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3653                                                               0.0, 1.0, 0.01, 0.1,
3654                                                               labels, values, G_N_ELEMENTS(labels),
3655                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3656         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3657         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3658         }
3660         {
3661         /* Mass */
3662             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3663         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3664         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3665                                                               _("Pen Mass"), _("Mass:"),
3666                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3667                                                               "tools.calligraphic", "mass", 0.02,
3668                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3669                                                               0.0, 1.0, 0.01, 0.1,
3670                                                               labels, values, G_N_ELEMENTS(labels),
3671                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3672         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3673         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3674         }
3677         /* Trace Background button */
3678         {
3679             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3680                                                           _("Trace Background"),
3681                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3682                                                           "trace_background",
3683                                                           Inkscape::ICON_SIZE_DECORATION );
3684             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3685             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3686             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3687             g_object_set_data( holder, "tracebackground", act );
3688         }
3690         /* Use Pressure button */
3691         {
3692             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3693                                                           _("Pressure"),
3694                                                           _("Use the pressure of the input device to alter the width of the pen"),
3695                                                           "use_pressure",
3696                                                           Inkscape::ICON_SIZE_DECORATION );
3697             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3698             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3699             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3700             g_object_set_data( holder, "usepressure", act );
3701         }
3703         /* Use Tilt button */
3704         {
3705             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3706                                                           _("Tilt"),
3707                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3708                                                           "use_tilt",
3709                                                           Inkscape::ICON_SIZE_DECORATION );
3710             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3711             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3712             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3713             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3714             g_object_set_data( holder, "usetilt", act );
3715         }
3717         /*calligraphic profile */
3718         {
3719             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3720             gchar *pref_path;
3723             GtkTreeIter iter;
3724             gtk_list_store_append( model, &iter );
3725             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3727             //TODO: switch back to prefs API
3728             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3729             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3730             int ii=1;
3731             while (child_repr) {
3732                 GtkTreeIter iter;
3733                 char *preset_name = (char *) child_repr->attribute("name");
3734                 gtk_list_store_append( model, &iter );                
3735                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
3736                 child_repr = sp_repr_next(child_repr); 
3737             }
3739             pref_path = NULL;
3740             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3741             ege_select_one_action_set_appearance( act1, "compact" );
3742             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3743             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3744             g_object_set_data( holder, "profile_selector", act1 );
3745         
3746         }
3748         /*Save or delete calligraphic profile */
3749         {
3750             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3751                                              _("Defaults"),
3752                                              _("Save current settings as new profile"),
3753                                              GTK_STOCK_SAVE );
3754             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3757             gtk_action_group_add_action( mainActions, act );
3758             gtk_action_set_sensitive( act, TRUE );
3759             g_object_set_data( holder, "profile_save_delete", act );
3760         }
3761     }    
3765 //########################
3766 //##    Circle / Arc    ##
3767 //########################
3769 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3771     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3772     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3774     if (v1 == 0 && v2 == 0) {
3775         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3776             gtk_action_set_sensitive( ocb, FALSE );
3777             gtk_action_set_sensitive( make_whole, FALSE );
3778         }
3779     } else {
3780         gtk_action_set_sensitive( ocb, TRUE );
3781         gtk_action_set_sensitive( make_whole, TRUE );
3782     }
3785 static void
3786 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3788     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3790     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3791         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3792     }
3794     // quit if run by the attr_changed listener
3795     if (g_object_get_data( tbl, "freeze" )) {
3796         return;
3797     }
3799     // in turn, prevent listener from responding
3800     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3802     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3804     bool modmade = false;
3805     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3806          items != NULL;
3807          items = items->next)
3808     {
3809         SPItem *item = SP_ITEM(items->data);
3811         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3813             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3814             SPArc *arc = SP_ARC(item);
3816             if (!strcmp(value_name, "start"))
3817                 ge->start = (adj->value * M_PI)/ 180;
3818             else
3819                 ge->end = (adj->value * M_PI)/ 180;
3821             sp_genericellipse_normalize(ge);
3822             ((SPObject *)arc)->updateRepr();
3823             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3825             modmade = true;
3826         }
3827     }
3829     g_free(namespaced_name);
3831     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3833     sp_arctb_sensitivize( tbl, adj->value, other->value );
3835     if (modmade) {
3836         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3837                                    _("Arc: Change start/end"));
3838     }
3840     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3844 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3846     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3849 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3851     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3854 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3856     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3857     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3858         if ( ege_select_one_action_get_active( act ) != 0 ) {
3859             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3860         } else {
3861             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3862         }
3863     }
3865     // quit if run by the attr_changed listener
3866     if (g_object_get_data( tbl, "freeze" )) {
3867         return;
3868     }
3870     // in turn, prevent listener from responding
3871     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3873     bool modmade = false;
3875     if ( ege_select_one_action_get_active(act) != 0 ) {
3876         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3877              items != NULL;
3878              items = items->next)
3879         {
3880             if (SP_IS_ARC((SPItem *) items->data)) {
3881                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3882                 repr->setAttribute("sodipodi:open", "true");
3883                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3884                 modmade = true;
3885             }
3886         }
3887     } else {
3888         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3889              items != NULL;
3890              items = items->next)
3891         {
3892             if (SP_IS_ARC((SPItem *) items->data))    {
3893                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3894                 repr->setAttribute("sodipodi:open", NULL);
3895                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3896                 modmade = true;
3897             }
3898         }
3899     }
3901     if (modmade) {
3902         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3903                                    _("Arc: Change open/closed"));
3904     }
3906     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3909 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3911     GtkAdjustment *adj;
3912     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3913     gtk_adjustment_set_value(adj, 0.0);
3914     gtk_adjustment_value_changed(adj);
3916     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3917     gtk_adjustment_set_value(adj, 0.0);
3918     gtk_adjustment_value_changed(adj);
3920     spinbutton_defocus( GTK_OBJECT(obj) );
3923 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3924                                       gchar const */*old_value*/, gchar const */*new_value*/,
3925                                       bool /*is_interactive*/, gpointer data)
3927     GObject *tbl = G_OBJECT(data);
3929     // quit if run by the _changed callbacks
3930     if (g_object_get_data( tbl, "freeze" )) {
3931         return;
3932     }
3934     // in turn, prevent callbacks from responding
3935     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3937     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3938     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3940     GtkAdjustment *adj1,*adj2;
3941     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3942     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3943     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3944     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3946     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3948     char const *openstr = NULL;
3949     openstr = repr->attribute("sodipodi:open");
3950     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3952     if (openstr) {
3953         ege_select_one_action_set_active( ocb, 1 );
3954     } else {
3955         ege_select_one_action_set_active( ocb, 0 );
3956     }
3958     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3961 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3962     NULL, /* child_added */
3963     NULL, /* child_removed */
3964     arc_tb_event_attr_changed,
3965     NULL, /* content_changed */
3966     NULL  /* order_changed */
3967 };
3970 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3972     int n_selected = 0;
3973     Inkscape::XML::Node *repr = NULL;
3975     purge_repr_listener( tbl, tbl );
3977     for (GSList const *items = selection->itemList();
3978          items != NULL;
3979          items = items->next)
3980     {
3981         if (SP_IS_ARC((SPItem *) items->data)) {
3982             n_selected++;
3983             repr = SP_OBJECT_REPR((SPItem *) items->data);
3984         }
3985     }
3987     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3989     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3990     if (n_selected == 0) {
3991         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3992     } else if (n_selected == 1) {
3993         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3994         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3996         if (repr) {
3997             g_object_set_data( tbl, "repr", repr );
3998             Inkscape::GC::anchor(repr);
3999             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4000             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4001         }
4002     } else {
4003         // FIXME: implement averaging of all parameters for multiple selected
4004         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4005         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4006         sp_arctb_sensitivize( tbl, 1, 0 );
4007     }
4011 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4013     EgeAdjustmentAction* eact = 0;
4016     {
4017         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4018         ege_output_action_set_use_markup( act, TRUE );
4019         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4020         g_object_set_data( holder, "mode_action", act );
4021     }
4023     /* Start */
4024     {
4025         eact = create_adjustment_action( "ArcStartAction",
4026                                          _("Start"), _("Start:"),
4027                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4028                                          "tools.shapes.arc", "start", 0.0,
4029                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4030                                          -360.0, 360.0, 1.0, 10.0,
4031                                          0, 0, 0,
4032                                          sp_arctb_start_value_changed);
4033         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4034     }
4036     /* End */
4037     {
4038         eact = create_adjustment_action( "ArcEndAction",
4039                                          _("End"), _("End:"),
4040                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4041                                          "tools.shapes.arc", "end", 0.0,
4042                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4043                                          -360.0, 360.0, 1.0, 10.0,
4044                                          0, 0, 0,
4045                                          sp_arctb_end_value_changed);
4046         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4047     }
4049     /* Segments / Pie checkbox */
4050     {
4051         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4053         GtkTreeIter iter;
4054         gtk_list_store_append( model, &iter );
4055         gtk_list_store_set( model, &iter,
4056                             0, _("Closed arc"),
4057                             1, _("Switch to segment (closed shape with two radii)"),
4058                             2, "circle_closed_arc",
4059                             -1 );
4061         gtk_list_store_append( model, &iter );
4062         gtk_list_store_set( model, &iter,
4063                             0, _("Open Arc"),
4064                             1, _("Switch to arc (unclosed shape)"),
4065                             2, "circle_open_arc",
4066                             -1 );
4068         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4069         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4070         g_object_set_data( holder, "open_action", act );
4072         ege_select_one_action_set_appearance( act, "full" );
4073         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4074         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4075         ege_select_one_action_set_icon_column( act, 2 );
4076         ege_select_one_action_set_tooltip_column( act, 1  );
4078         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4079         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4080         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4081         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4082     }
4084     /* Make Whole */
4085     {
4086         InkAction* inky = ink_action_new( "ArcResetAction",
4087                                           _("Make whole"),
4088                                           _("Make the shape a whole ellipse, not arc or segment"),
4089                                           "reset_circle",
4090                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4091         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4092         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4093         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4094         g_object_set_data( holder, "make_whole", inky );
4095     }
4097     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4098     // sensitivize make whole and open checkbox
4099     {
4100         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4101         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4102         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4103     }
4106     sigc::connection *connection = new sigc::connection(
4107         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4108         );
4109     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4110     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4116 // toggle button callbacks and updaters
4118 //########################
4119 //##      Dropper       ##
4120 //########################
4122 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4123     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4124     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4125     if ( set_action ) {
4126         if ( gtk_toggle_action_get_active( act ) ) {
4127             gtk_action_set_sensitive( set_action, TRUE );
4128         } else {
4129             gtk_action_set_sensitive( set_action, FALSE );
4130         }
4131     }
4133     spinbutton_defocus(GTK_OBJECT(tbl));
4136 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4137     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4138     spinbutton_defocus(GTK_OBJECT(tbl));
4142 /**
4143  * Dropper auxiliary toolbar construction and setup.
4144  *
4145  * TODO: Would like to add swatch of current color.
4146  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4147  *       can drag and drop places. Will provide a nice mixing palette.
4148  */
4149 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4151     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4153     {
4154         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4155         ege_output_action_set_use_markup( act, TRUE );
4156         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4157     }
4159     {
4160         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4161                                                       _("Pick opacity"),
4162                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4163                                                       NULL,
4164                                                       Inkscape::ICON_SIZE_DECORATION );
4165         g_object_set( act, "short_label", _("Pick"), NULL );
4166         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4167         g_object_set_data( holder, "pick_action", act );
4168         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4169         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4170     }
4172     {
4173         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4174                                                       _("Assign opacity"),
4175                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4176                                                       NULL,
4177                                                       Inkscape::ICON_SIZE_DECORATION );
4178         g_object_set( act, "short_label", _("Assign"), NULL );
4179         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4180         g_object_set_data( holder, "set_action", act );
4181         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4182         // make sure it's disabled if we're not picking alpha
4183         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4184         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4185     }
4189 //########################
4190 //##    Text Toolbox    ##
4191 //########################
4192 /*
4193 static void
4194 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4196     //Call back for letter sizing spinbutton
4199 static void
4200 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4202     //Call back for line height spinbutton
4205 static void
4206 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4208     //Call back for horizontal kerning spinbutton
4211 static void
4212 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4214     //Call back for vertical kerning spinbutton
4217 static void
4218 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4220     //Call back for letter rotation spinbutton
4221 }*/
4223 namespace {
4225 bool popdown_visible = false;
4226 bool popdown_hasfocus = false;
4228 void
4229 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4231     SPStyle *query =
4232         sp_style_new (SP_ACTIVE_DOCUMENT);
4234     int result_fontspec =
4235         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4237     int result_family =
4238         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4240     int result_style =
4241         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4243     int result_numbers =
4244         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4246     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4248     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4249     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4250     {
4251         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4253         if (repr)
4254         {
4255             sp_style_read_from_repr (query, repr);
4256         }
4257         else
4258         {
4259             return;
4260         }
4261     }
4263     if (query->text)
4264     {
4265         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4266             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4267             gtk_entry_set_text (GTK_ENTRY (entry), "");
4269         } else if (query->text->font_specification.value || query->text->font_family.value) {
4271             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4273             // Get the font that corresponds
4274             Glib::ustring familyName;
4276             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4277             if (font) {
4278                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4279                 font->Unref();
4280                 font = NULL;
4281             }
4283             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4285             Gtk::TreePath path;
4286             try {
4287                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4288             } catch (...) {
4289                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4290                 return;
4291             }
4293             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4294             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4296             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4298             gtk_tree_selection_select_path (tselection, path.gobj());
4299             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4301             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4302         }
4304         //Size
4305         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4306         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4307         g_object_set_data (tbl, "size-block", gpointer(1));
4308         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4309         g_object_set_data (tbl, "size-block", gpointer(0));
4310         free (str);
4312         //Anchor
4313         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4314         {
4315             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4316             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4317             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4318             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4319         }
4320         else
4321         {
4322             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4323             {
4324                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4325                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4326                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4327                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4328             }
4329             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4330             {
4331                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4332                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4333                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4334                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4335             }
4336             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4337             {
4338                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4339                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4340                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4341                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4342             }
4343         }
4345         //Style
4346         {
4347             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4349             gboolean active = gtk_toggle_button_get_active (button);
4350             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4352             if (active != check)
4353             {
4354                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4355                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4356                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4357             }
4358         }
4360         {
4361             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4363             gboolean active = gtk_toggle_button_get_active (button);
4364             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4366             if (active != check)
4367             {
4368                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4369                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4370                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4371             }
4372         }
4374         //Orientation
4375         //locking both buttons, changing one affect all group (both)
4376         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4377         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4379         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4380         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4382         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4383         {
4384             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4385         }
4386         else
4387         {
4388             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4389         }
4390         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4391         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4392     }
4394     sp_style_unref(query);
4397 void
4398 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4400     sp_text_toolbox_selection_changed (selection, tbl);
4403 void
4404 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4406     sp_text_toolbox_selection_changed (NULL, tbl);
4409 void
4410 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4411                                 GObject             *tbl)
4413     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4414     GtkTreeModel *model = 0;
4415     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4416     GtkTreeIter   iter;
4417     char         *family = 0;
4419     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4420     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4422     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4423         return;
4424     }
4426     gtk_tree_model_get (model, &iter, 0, &family, -1);
4428     if (g_object_get_data (G_OBJECT (selection), "block"))
4429     {
4430         gtk_entry_set_text (GTK_ENTRY (entry), family);
4431         return;
4432     }
4434     gtk_entry_set_text (GTK_ENTRY (entry), family);
4436     SPStyle *query =
4437         sp_style_new (SP_ACTIVE_DOCUMENT);
4439     int result_fontspec =
4440         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4442     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4444     SPCSSAttr *css = sp_repr_css_attr_new ();
4447     // First try to get the font spec from the stored value
4448     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4450     if (fontSpec.empty()) {
4451         // Construct a new font specification if it does not yet exist
4452         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4453         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4454         fontFromStyle->Unref();
4455     }
4457     if (!fontSpec.empty()) {
4458         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4459         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4460             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4461             if (font) {
4462                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4464                 // Set all the these just in case they were altered when finding the best
4465                 // match for the new family and old style...
4467                 gchar c[256];
4469                 font->Family(c, 256);
4470                 sp_repr_css_set_property (css, "font-family", c);
4472                 font->Attribute( "weight", c, 256);
4473                 sp_repr_css_set_property (css, "font-weight", c);
4475                 font->Attribute("style", c, 256);
4476                 sp_repr_css_set_property (css, "font-style", c);
4478                 font->Attribute("stretch", c, 256);
4479                 sp_repr_css_set_property (css, "font-stretch", c);
4481                 font->Attribute("variant", c, 256);
4482                 sp_repr_css_set_property (css, "font-variant", c);
4484                 font->Unref();
4485             }
4486         }
4487     }
4489     // If querying returned nothing, set the default style of the tool (for new texts)
4490     if (result_fontspec == QUERY_STYLE_NOTHING)
4491     {
4492         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4493         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4494     }
4495     else
4496     {
4497         sp_desktop_set_style (desktop, css, true, true);
4498     }
4500     sp_style_unref(query);
4502     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4503                                    _("Text: Change font family"));
4504     sp_repr_css_attr_unref (css);
4505     free (family);
4506     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4508     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4511 void
4512 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4513                                        GObject      *tbl)
4515     const char *family = gtk_entry_get_text (entry);
4517     try {
4518         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4519         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4520         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4521         gtk_tree_selection_select_path (selection, path.gobj());
4522         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4523         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4524     } catch (...) {
4525         if (family && strlen (family))
4526         {
4527             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4528         }
4529     }
4532 void
4533 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4534                                    gpointer          data)
4536     if (g_object_get_data (G_OBJECT (button), "block")) return;
4537     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4538     int prop = GPOINTER_TO_INT(data);
4540     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4541     SPCSSAttr *css = sp_repr_css_attr_new ();
4543     switch (prop)
4544     {
4545         case 0:
4546         {
4547             sp_repr_css_set_property (css, "text-anchor", "start");
4548             sp_repr_css_set_property (css, "text-align", "start");
4549             break;
4550         }
4551         case 1:
4552         {
4553             sp_repr_css_set_property (css, "text-anchor", "middle");
4554             sp_repr_css_set_property (css, "text-align", "center");
4555             break;
4556         }
4558         case 2:
4559         {
4560             sp_repr_css_set_property (css, "text-anchor", "end");
4561             sp_repr_css_set_property (css, "text-align", "end");
4562             break;
4563         }
4565         case 3:
4566         {
4567             sp_repr_css_set_property (css, "text-anchor", "start");
4568             sp_repr_css_set_property (css, "text-align", "justify");
4569             break;
4570         }
4571     }
4573     SPStyle *query =
4574         sp_style_new (SP_ACTIVE_DOCUMENT);
4575     int result_numbers =
4576         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4578     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4579     if (result_numbers == QUERY_STYLE_NOTHING)
4580     {
4581         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4582     }
4584     sp_style_unref(query);
4586     sp_desktop_set_style (desktop, css, true, true);
4587     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4588                                    _("Text: Change alignment"));
4589     sp_repr_css_attr_unref (css);
4591     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4594 void
4595 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4596                                gpointer          data)
4598     if (g_object_get_data (G_OBJECT (button), "block")) return;
4600     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4601     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4602     int          prop       = GPOINTER_TO_INT(data);
4603     bool         active     = gtk_toggle_button_get_active (button);
4605     SPStyle *query =
4606         sp_style_new (SP_ACTIVE_DOCUMENT);
4608     int result_fontspec =
4609         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4611     int result_family =
4612         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4614     int result_style =
4615         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4617     int result_numbers =
4618         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4620     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4621     Glib::ustring newFontSpec = "";
4623     if (fontSpec.empty()) {
4624         // Construct a new font specification if it does not yet exist
4625         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4626         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4627         fontFromStyle->Unref();
4628     }
4630     switch (prop)
4631     {
4632         case 0:
4633         {
4634             if (!fontSpec.empty()) {
4635                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4636             }
4637             if (fontSpec != newFontSpec) {
4638                 // Don't even set the bold if the font didn't exist on the system
4639                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4640             }
4641             break;
4642         }
4644         case 1:
4645         {
4646             if (!fontSpec.empty()) {
4647                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4648             }
4649             if (fontSpec != newFontSpec) {
4650                 // Don't even set the italic if the font didn't exist on the system
4651                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4652             }
4653             break;
4654         }
4655     }
4657     if (!newFontSpec.empty()) {
4658         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4659     }
4661     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4662     if (result_fontspec == QUERY_STYLE_NOTHING)
4663     {
4664         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4665     }
4667     sp_style_unref(query);
4669     sp_desktop_set_style (desktop, css, true, true);
4670     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4671                                    _("Text: Change font style"));
4672     sp_repr_css_attr_unref (css);
4674     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4677 void
4678 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4679                                      gpointer         data)
4681     if (g_object_get_data (G_OBJECT (button), "block")) {
4682         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4683         return;
4684     }
4686     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4687     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4688     int          prop       = GPOINTER_TO_INT(data);
4690     switch (prop)
4691     {
4692         case 0:
4693         {
4694             sp_repr_css_set_property (css, "writing-mode", "lr");
4695             break;
4696         }
4698         case 1:
4699         {
4700             sp_repr_css_set_property (css, "writing-mode", "tb");
4701             break;
4702         }
4703     }
4705     SPStyle *query =
4706         sp_style_new (SP_ACTIVE_DOCUMENT);
4707     int result_numbers =
4708         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4710     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4711     if (result_numbers == QUERY_STYLE_NOTHING)
4712     {
4713         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4714     }
4716     sp_desktop_set_style (desktop, css, true, true);
4717     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4718                                    _("Text: Change orientation"));
4719     sp_repr_css_attr_unref (css);
4721     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4724 gboolean
4725 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4727     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4728     if (!desktop) return FALSE;
4730     switch (get_group0_keyval (event)) {
4731         case GDK_Escape: // defocus
4732             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4733             sp_text_toolbox_selection_changed (NULL, tbl); // update
4734             return TRUE; // I consumed the event
4735             break;
4736     }
4737     return FALSE;
4740 gboolean
4741 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4743     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4744     if (!desktop) return FALSE;
4746     switch (get_group0_keyval (event)) {
4747         case GDK_KP_Enter:
4748         case GDK_Return:
4749         case GDK_Escape: // defocus
4750             gtk_widget_hide (w);
4751             popdown_visible = false;
4752             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4753             return TRUE; // I consumed the event
4754             break;
4755         case GDK_w:
4756         case GDK_W:
4757             if (event->state & GDK_CONTROL_MASK) {
4758                 gtk_widget_hide (w);
4759                 popdown_visible = false;
4760                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4761                 return TRUE; // I consumed the event
4762             }
4763             break;
4764     }
4765     return FALSE;
4769 void
4770 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4771                                GObject     *tbl)
4773     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4775     if (g_object_get_data (tbl, "size-block")) return;
4777     // If this is not from selecting a size in the list (in which case get_active will give the
4778     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4779     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4780     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4781     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4782         return;
4784     gchar *endptr;
4785     gdouble value = -1;
4786     char *text = gtk_combo_box_get_active_text (cbox);
4787     if (text) {
4788         value = g_strtod (text, &endptr);
4789         if (endptr == text) // conversion failed, non-numeric input
4790             value = -1;
4791         free (text);
4792     }
4793     if (value <= 0) {
4794         return; // could not parse value
4795     }
4797     SPCSSAttr *css = sp_repr_css_attr_new ();
4798     Inkscape::CSSOStringStream osfs;
4799     osfs << value;
4800     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4802     SPStyle *query =
4803         sp_style_new (SP_ACTIVE_DOCUMENT);
4804     int result_numbers =
4805         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4807     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4808     if (result_numbers == QUERY_STYLE_NOTHING)
4809     {
4810         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4811     }
4813     sp_style_unref(query);
4815     sp_desktop_set_style (desktop, css, true, true);
4816     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4817                                    _("Text: Change font size"));
4818     sp_repr_css_attr_unref (css);
4820     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4823 gboolean
4824 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4826     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4827     if (!desktop) return FALSE;
4829     if (!g_object_get_data (tbl, "esc-pressed")) {
4830         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4831         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4832         sp_text_toolbox_size_changed (cbox, tbl);
4833         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4834     }
4835     return FALSE; // I consumed the event
4839 gboolean
4840 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4842     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4843     if (!desktop) return FALSE;
4845     switch (get_group0_keyval (event)) {
4846         case GDK_Escape: // defocus
4847             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4848             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4849             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4850             return TRUE; // I consumed the event
4851             break;
4852         case GDK_Return: // defocus
4853         case GDK_KP_Enter:
4854             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4855             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4856             sp_text_toolbox_size_changed (cbox, tbl);
4857             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4858             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4859             return TRUE; // I consumed the event
4860             break;
4861     }
4862     return FALSE;
4865 void
4866 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4867                                          GObject            *tbl)
4869     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4870     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4871     int x, y;
4873     if (!popdown_visible)
4874     {
4875         gdk_window_get_origin (widget->window, &x, &y);
4876         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4877         gtk_widget_show_all (popdown);
4878         //sp_transientize (popdown);
4880         gdk_pointer_grab (widget->window, TRUE,
4881                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4882                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4883                                         GDK_POINTER_MOTION_MASK),
4884                           NULL, NULL, GDK_CURRENT_TIME);
4886         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4888         popdown_visible = true;
4889     }
4890     else
4891     {
4892         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4893         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4894         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4895         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4896         gtk_widget_hide (popdown);
4897         popdown_visible = false;
4898     }
4901 gboolean
4902 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4903                                  GdkEventFocus    */*event*/,
4904                                  GObject          */*tbl*/)
4906     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4907     return FALSE;
4910 gboolean
4911 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4912                                    GdkEventFocus    */*event*/,
4913                                    GObject          */*tbl*/)
4915     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4917     if (popdown_hasfocus) {
4918         gtk_widget_hide (popdown);
4919         popdown_hasfocus = false;
4920         popdown_visible = false;
4921         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4922         return TRUE;
4923     }
4924     return FALSE;
4927 gboolean
4928 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
4929                                    GdkEventFocus    */*event*/,
4930                                    GObject          */*tbl*/)
4932     popdown_hasfocus = true;
4933     return TRUE;
4937 void
4938 cell_data_func  (GtkTreeViewColumn */*column*/,
4939                  GtkCellRenderer   *cell,
4940                  GtkTreeModel      *tree_model,
4941                  GtkTreeIter       *iter,
4942                  gpointer           /*data*/)
4944     char        *family,
4945         *family_escaped,
4946         *sample_escaped;
4948     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4950     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4952     family_escaped = g_markup_escape_text (family, -1);
4953     sample_escaped = g_markup_escape_text (sample, -1);
4955     std::stringstream markup;
4956     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4957     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4959     free (family);
4960     free (family_escaped);
4961     free (sample_escaped);
4964 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4965     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4966     if (completion) {
4967         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4968         g_object_unref (completion);
4969     }
4972 GtkWidget*
4973 sp_text_toolbox_new (SPDesktop *desktop)
4975     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4977     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4978     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4980     GtkTooltips *tt = gtk_tooltips_new();
4981     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4983     ////////////Family
4984     //Window
4985     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4986     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4988     //Entry
4989     GtkWidget           *entry = gtk_entry_new ();
4990     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4991     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4992     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4993     gtk_entry_completion_set_text_column (completion, 0);
4994     gtk_entry_completion_set_minimum_key_length (completion, 1);
4995     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4996     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4997     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4998     aux_toolbox_space (tbl, 1);
4999     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5000     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5002     //Button
5003     GtkWidget   *button = gtk_button_new ();
5004     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5005     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5007     //Popdown
5008     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5009     GtkWidget           *treeview = gtk_tree_view_new ();
5011     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5012     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5013     gtk_tree_view_column_pack_start (column, cell, FALSE);
5014     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5015     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5016     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5018     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5019     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5020     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5022     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5024     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5025     gtk_container_add (GTK_CONTAINER (sw), treeview);
5027     gtk_container_add (GTK_CONTAINER (window), sw);
5028     gtk_widget_set_size_request (window, 300, 450);
5030     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5031     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5032     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5034     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5036     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5037     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5038     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5040     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5041     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5043     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5044     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5045     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5046     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5047     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5049     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
5050     aux_toolbox_space (tbl, 1);
5051     GtkWidget *box = gtk_event_box_new ();
5052     gtk_container_add (GTK_CONTAINER (box), image);
5053     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5054     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5055     GtkTooltips *tooltips = gtk_tooltips_new ();
5056     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5057     gtk_widget_hide (GTK_WIDGET (box));
5058     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5060     ////////////Size
5061     const char *sizes[] = {
5062         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5063         "16", "18", "20", "22", "24", "28",
5064         "32", "36", "40", "48", "56", "64", "72", "144"
5065     };
5067     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5068     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5069     gtk_widget_set_size_request (cbox, 80, -1);
5070     aux_toolbox_space (tbl, 1);
5071     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5072     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5073     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5074     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5075     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5077     //spacer
5078     aux_toolbox_space (tbl, 4);
5079     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5081     ////////////Text anchor
5082     GtkWidget *group   = gtk_radio_button_new (NULL);
5083     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5084     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5086     // left
5087     GtkWidget *rbutton = group;
5088     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5089     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5090     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5092     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5093     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5094     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5095     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5097     // center
5098     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5099     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5100     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
5101     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5103     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5104     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5105     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5106     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5108     // right
5109     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5110     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5111     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5112     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5114     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5115     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5116     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5117     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5119     // fill
5120     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5121     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5122     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5123     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5125     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5126     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5127     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5128     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5130     aux_toolbox_space (tbl, 1);
5131     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5133     //spacer
5134     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5136     ////////////Text style
5137     row = gtk_hbox_new (FALSE, 4);
5139     // bold
5140     rbutton = gtk_toggle_button_new ();
5141     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5142     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5143     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5144     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5146     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5147     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5148     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5150     // italic
5151     rbutton = gtk_toggle_button_new ();
5152     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5153     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5154     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5155     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5157     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5158     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5159     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5161     aux_toolbox_space (tbl, 1);
5162     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5164     //spacer
5165     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5167     ////////////Text orientation
5168     group   = gtk_radio_button_new (NULL);
5169     row     = gtk_hbox_new (FALSE, 4);
5170     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5172     // horizontal
5173     rbutton = group;
5174     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5175     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5176     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5177     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5179     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5180     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5181     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5183     // vertical
5184     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5185     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5186     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5187     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5188     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5190     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5191     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5192     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5193     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5196     //watch selection
5197     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5199     sigc::connection *c_selection_changed =
5200         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5201                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5202     pool->add_connection ("selection-changed", c_selection_changed);
5204     sigc::connection *c_selection_modified =
5205         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5206                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5207     pool->add_connection ("selection-modified", c_selection_modified);
5209     sigc::connection *c_subselection_changed =
5210         new sigc::connection (desktop->connectToolSubselectionChanged
5211                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5212     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5214     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5217     gtk_widget_show_all (tbl);
5218     return tbl;
5220 } // end of sp_text_toolbox_new()
5222 }//<unnamed> namespace
5225 //#########################
5226 //##      Connector      ##
5227 //#########################
5229 static void sp_connector_path_set_avoid(void)
5231     cc_selection_set_avoid(true);
5235 static void sp_connector_path_set_ignore(void)
5237     cc_selection_set_avoid(false);
5242 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5244     // quit if run by the _changed callbacks
5245     if (g_object_get_data( tbl, "freeze" )) {
5246         return;
5247     }
5249     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5250     SPDocument *doc = sp_desktop_document(desktop);
5252     if (!sp_document_get_undo_sensitive(doc))
5253     {
5254         return;
5255     }
5257     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5259     if ( repr->attribute("inkscape:connector-spacing") ) {
5260         gdouble priorValue = gtk_adjustment_get_value(adj);
5261         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5262         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5263             return;
5264         }
5265     } else if ( adj->value == defaultConnSpacing ) {
5266         return;
5267     }
5269     // in turn, prevent callbacks from responding
5270     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5272     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5273     SP_OBJECT(desktop->namedview)->updateRepr();
5275     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5276     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5277         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5278         NR::Matrix m = NR::identity();
5279         avoid_item_move(&m, item);
5280     }
5282     if (items) {
5283         g_slist_free(items);
5284     }
5286     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5287             _("Change connector spacing"));
5289     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5291     spinbutton_defocus(GTK_OBJECT(tbl));
5294 static void sp_connector_graph_layout(void)
5296     if (!SP_ACTIVE_DESKTOP) return;
5298     // hack for clones, see comment in align-and-distribute.cpp
5299     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5300     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5302     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5304     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5306     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5309 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5311     if ( gtk_toggle_action_get_active( act ) ) {
5312         prefs_set_string_attribute("tools.connector", "directedlayout",
5313                 "true");
5314     } else {
5315         prefs_set_string_attribute("tools.connector", "directedlayout",
5316                 "false");
5317     }
5320 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5322     if ( gtk_toggle_action_get_active( act ) ) {
5323         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5324                 "true");
5325     } else {
5326         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5327                 "false");
5328     }
5332 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5334     prefs_set_double_attribute("tools.connector", "length", adj->value);
5335     spinbutton_defocus(GTK_OBJECT(tbl));
5338 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5339                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5340                                             bool /*is_interactive*/, gpointer data)
5342     GtkWidget *tbl = GTK_WIDGET(data);
5344     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5345         return;
5346     }
5347     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5348         return;
5349     }
5351     GtkAdjustment *adj = (GtkAdjustment*)
5352             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5353     gdouble spacing = defaultConnSpacing;
5354     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5356     gtk_adjustment_set_value(adj, spacing);
5360 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5361     NULL, /* child_added */
5362     NULL, /* child_removed */
5363     connector_tb_event_attr_changed,
5364     NULL, /* content_changed */
5365     NULL  /* order_changed */
5366 };
5369 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5371     {
5372         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5373                                           _("Avoid"),
5374                                           _("Make connectors avoid selected objects"),
5375                                           "connector_avoid",
5376                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5377         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5378         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5379     }
5381     {
5382         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5383                                           _("Ignore"),
5384                                           _("Make connectors ignore selected objects"),
5385                                           "connector_ignore",
5386                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5387         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5388         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5389     }
5391     EgeAdjustmentAction* eact = 0;
5393     // Spacing spinbox
5394     eact = create_adjustment_action( "ConnectorSpacingAction",
5395                                      _("Connector Spacing"), _("Spacing:"),
5396                                      _("The amount of space left around objects by auto-routing connectors"),
5397                                      "tools.connector", "spacing", defaultConnSpacing,
5398                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5399                                      0, 100, 1.0, 10.0,
5400                                      0, 0, 0,
5401                                      connector_spacing_changed, 1, 0 );
5402     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5404     // Graph (connector network) layout
5405     {
5406         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5407                                           _("Graph"),
5408                                           _("Nicely arrange selected connector network"),
5409                                           "graph_layout",
5410                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5411         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5412         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5413     }
5415     // Default connector length spinbox
5416     eact = create_adjustment_action( "ConnectorLengthAction",
5417                                      _("Connector Length"), _("Length:"),
5418                                      _("Ideal length for connectors when layout is applied"),
5419                                      "tools.connector", "length", 100,
5420                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5421                                      10, 1000, 10.0, 100.0,
5422                                      0, 0, 0,
5423                                      connector_length_changed, 1, 0 );
5424     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5427     // Directed edges toggle button
5428     {
5429         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5430                                                       _("Downwards"),
5431                                                       _("Make connectors with end-markers (arrows) point downwards"),
5432                                                       "directed_graph",
5433                                                       Inkscape::ICON_SIZE_DECORATION );
5434         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5436         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5437         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5438                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5440         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5441     }
5443     // Avoid overlaps toggle button
5444     {
5445         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5446                                                       _("Remove overlaps"),
5447                                                       _("Do not allow overlapping shapes"),
5448                                                       "remove_overlaps",
5449                                                       Inkscape::ICON_SIZE_DECORATION );
5450         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5452         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5453         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5454                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5456         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5457     }
5459     // Code to watch for changes to the connector-spacing attribute in
5460     // the XML.
5461     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5462     g_assert(repr != NULL);
5464     purge_repr_listener( holder, holder );
5466     if (repr) {
5467         g_object_set_data( holder, "repr", repr );
5468         Inkscape::GC::anchor(repr);
5469         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5470         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5471     }
5472 } // end of sp_connector_toolbox_prep()
5475 //#########################
5476 //##     Paintbucket     ##
5477 //#########################
5479 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5481     gint channels = ege_select_one_action_get_active( act );
5482     flood_channels_set_channels( channels );
5485 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5487     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5490 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5492     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5495 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5497     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5498     SPUnit const *unit = tracker->getActiveUnit();
5500     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5502     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5505 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5507     // FIXME: make defaults settable via Inkscape Options
5508     struct KeyValue {
5509         char const *key;
5510         double value;
5511     } const key_values[] = {
5512         {"threshold", 15},
5513         {"offset", 0.0}
5514     };
5516     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5517         KeyValue const &kv = key_values[i];
5518         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5519         if ( adj ) {
5520             gtk_adjustment_set_value(adj, kv.value);
5521         }
5522     }
5524     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5525     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5526     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5527     ege_select_one_action_set_active( autogap_action, 0 );
5530 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5532     EgeAdjustmentAction* eact = 0;
5534     {
5535         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5537         GList* items = 0;
5538         gint count = 0;
5539         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5540         {
5541             GtkTreeIter iter;
5542             gtk_list_store_append( model, &iter );
5543             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5544             count++;
5545         }
5546         g_list_free( items );
5547         items = 0;
5548         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5549         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5550         ege_select_one_action_set_appearance( act1, "compact" );
5551         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5552         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5553         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5554         g_object_set_data( holder, "channels_action", act1 );
5555     }
5557     // Spacing spinbox
5558     {
5559         eact = create_adjustment_action(
5560             "ThresholdAction",
5561             _("Fill Threshold"), _("Threshold:"),
5562             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5563             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5564             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5565             0, 0, 0,
5566             paintbucket_threshold_changed, 1, 0 );
5568         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5569     }
5571     // Create the units menu.
5572     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5573     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5574     if (stored_unit)
5575         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5576     g_object_set_data( holder, "tracker", tracker );
5577     {
5578         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5579         gtk_action_group_add_action( mainActions, act );
5580     }
5582     // Offset spinbox
5583     {
5584         eact = create_adjustment_action(
5585             "OffsetAction",
5586             _("Grow/shrink by"), _("Grow/shrink by:"),
5587             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5588             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5589             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5590             0, 0, 0,
5591             paintbucket_offset_changed, 1, 2);
5592         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5594         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5595     }
5597     /* Auto Gap */
5598     {
5599         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5601         GList* items = 0;
5602         gint count = 0;
5603         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5604         {
5605             GtkTreeIter iter;
5606             gtk_list_store_append( model, &iter );
5607             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5608             count++;
5609         }
5610         g_list_free( items );
5611         items = 0;
5612         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5613         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5614         ege_select_one_action_set_appearance( act2, "compact" );
5615         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5616         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5617         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5618         g_object_set_data( holder, "autogap_action", act2 );
5619     }
5621     /* Reset */
5622     {
5623         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5624                                           _("Defaults"),
5625                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5626                                           GTK_STOCK_CLEAR );
5627         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5628         gtk_action_group_add_action( mainActions, act );
5629         gtk_action_set_sensitive( act, TRUE );
5630     }
5634 /*
5635   Local Variables:
5636   mode:c++
5637   c-file-style:"stroustrup"
5638   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5639   indent-tabs-mode:nil
5640   fill-column:99
5641   End:
5642 */
5643 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :