Code

3609a48dd981ff6c1b92e33c1b803532699b707f
[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 <gtkmm.h>
31 #include <gtk/gtk.h>
32 #include <iostream>
33 #include <sstream>
35 #include "widgets/button.h"
36 #include "widgets/widget-sizes.h"
37 #include "widgets/spw-utilities.h"
38 #include "widgets/spinbutton-events.h"
39 #include "dialogs/text-edit.h"
41 #include "ui/widget/style-swatch.h"
43 #include "prefs-utils.h"
44 #include "verbs.h"
45 #include "sp-namedview.h"
46 #include "desktop.h"
47 #include "desktop-handles.h"
48 #include "xml/repr.h"
49 #include "xml/node-event-vector.h"
50 #include <glibmm/i18n.h>
51 #include "helper/unit-menu.h"
52 #include "helper/units.h"
54 #include "inkscape.h"
55 #include "conn-avoid-ref.h"
58 #include "select-toolbar.h"
59 #include "gradient-toolbar.h"
61 #include "connector-context.h"
62 #include "node-context.h"
63 #include "shape-editor.h"
64 #include "tweak-context.h"
65 #include "sp-rect.h"
66 #include "box3d.h"
67 #include "box3d-context.h"
68 #include "sp-star.h"
69 #include "sp-spiral.h"
70 #include "sp-ellipse.h"
71 #include "sp-text.h"
72 #include "sp-flowtext.h"
73 #include "style.h"
74 #include "selection.h"
75 #include "document-private.h"
76 #include "desktop-style.h"
77 #include "../libnrtype/font-lister.h"
78 #include "../connection-pool.h"
79 #include "../prefs-utils.h"
80 #include "../inkscape-stock.h"
81 #include "icon.h"
82 #include "graphlayout/graphlayout.h"
84 #include "mod360.h"
86 #include "toolbox.h"
88 #include "flood-context.h"
90 #include "ink-action.h"
91 #include "ege-adjustment-action.h"
92 #include "ege-output-action.h"
93 #include "ege-select-one-action.h"
94 #include "helper/unit-tracker.h"
96 using Inkscape::UnitTracker;
98 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
99 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
101 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
102 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
103 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
114 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
120 static struct {
121     gchar const *type_name;
122     gchar const *data_name;
123     sp_verb_t verb;
124     sp_verb_t doubleclick_verb;
125 } const tools[] = {
126     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
127     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
128     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
129     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
130     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
131 //    { "SP3DBoxContext",    "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
132     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
133     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
134     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
135     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
136     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
137     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
138     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
139     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
140     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
141     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
142     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
143     { NULL, NULL, 0, 0 }
144 };
146 static struct {
147     gchar const *type_name;
148     gchar const *data_name;
149     GtkWidget *(*create_func)(SPDesktop *desktop);
150     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
151     gchar const *ui_name;
152     gint swatch_verb_id;
153     gchar const *swatch_tool;
154     gchar const *swatch_tip;
155 } const aux_toolboxes[] = {
156     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
157       SP_VERB_INVALID, 0, 0},
158     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
159       SP_VERB_INVALID, 0, 0},
160     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
161       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
162     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
163       SP_VERB_INVALID, 0, 0},
164     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
165       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
166     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
167       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
168     { "SP3DBoxContext",  "3dbox_toolbox",  0, sp_3dbox_toolbox_prep,             "3DBoxToolbar",
169       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
170     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
171       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
172     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
173       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
174     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
175       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
176     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
177       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
178     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
179       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
180     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
181       SP_VERB_INVALID, 0, 0},
182     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
183       SP_VERB_INVALID, 0, 0},
184     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
185       SP_VERB_INVALID, 0, 0},
186     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
187       SP_VERB_INVALID, 0, 0},
188     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
189       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
190     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
191 };
194 static gchar const * ui_descr =
195         "<ui>"
196         "  <toolbar name='SelectToolbar'>"
197         "    <toolitem action='EditSelectAll' />"
198         "    <toolitem action='EditSelectAllInAllLayers' />"
199         "    <toolitem action='EditDeselect' />"
200         "    <separator />"
201         "    <toolitem action='ObjectRotate90CCW' />"
202         "    <toolitem action='ObjectRotate90' />"
203         "    <toolitem action='ObjectFlipHorizontally' />"
204         "    <toolitem action='ObjectFlipVertically' />"
205         "    <separator />"
206         "    <toolitem action='SelectionToBack' />"
207         "    <toolitem action='SelectionLower' />"
208         "    <toolitem action='SelectionRaise' />"
209         "    <toolitem action='SelectionToFront' />"
210         "    <separator />"
211         "    <toolitem action='XAction' />"
212         "    <toolitem action='YAction' />"
213         "    <toolitem action='WidthAction' />"
214         "    <toolitem action='LockAction' />"
215         "    <toolitem action='HeightAction' />"
216         "    <toolitem action='UnitsAction' />"
217         "    <separator />"
218         "    <toolitem action='transform_affect_label' />"
219         "    <toolitem action='transform_stroke' />"
220         "    <toolitem action='transform_corners' />"
221         "    <toolitem action='transform_gradient' />"
222         "    <toolitem action='transform_pattern' />"
223         "  </toolbar>"
225         "  <toolbar name='NodeToolbar'>"
226         "    <toolitem action='NodeInsertAction' />"
227         "    <toolitem action='NodeDeleteAction' />"
228         "    <separator />"
229         "    <toolitem action='NodeJoinAction' />"
230         "    <toolitem action='NodeJoinSegmentAction' />"
231         "    <toolitem action='NodeDeleteSegmentAction' />"
232         "    <toolitem action='NodeBreakAction' />"
233         "    <separator />"
234         "    <toolitem action='NodeCuspAction' />"
235         "    <toolitem action='NodeSmoothAction' />"
236         "    <toolitem action='NodeSymmetricAction' />"
237         "    <separator />"
238         "    <toolitem action='NodeLineAction' />"
239         "    <toolitem action='NodeCurveAction' />"
240         "    <separator />"
241         "    <toolitem action='ObjectToPath' />"
242         "    <toolitem action='StrokeToPath' />"
243         "    <separator />"
244         "    <toolitem action='NodesShowHandlesAction' />"
245         "  </toolbar>"
247         "  <toolbar name='TweakToolbar'>"
248         "    <toolitem action='TweakWidthAction' />"
249         "    <separator />"
250         "    <toolitem action='TweakForceAction' />"
251         "    <toolitem action='TweakPressureAction' />"
252         "    <separator />"
253         "    <toolitem action='TweakModeAction' />"
254         "    <separator />"
255         "    <toolitem action='TweakFidelityAction' />"
256         "    <separator />"
257         "    <toolitem action='TweakChannelsLabel' />"
258         "    <toolitem action='TweakDoH' />"
259         "    <toolitem action='TweakDoS' />"
260         "    <toolitem action='TweakDoL' />"
261         "    <toolitem action='TweakDoO' />"
262         "  </toolbar>"
264         "  <toolbar name='ZoomToolbar'>"
265         "    <toolitem action='ZoomIn' />"
266         "    <toolitem action='ZoomOut' />"
267         "    <separator />"
268         "    <toolitem action='Zoom1:0' />"
269         "    <toolitem action='Zoom1:2' />"
270         "    <toolitem action='Zoom2:1' />"
271         "    <separator />"
272         "    <toolitem action='ZoomSelection' />"
273         "    <toolitem action='ZoomDrawing' />"
274         "    <toolitem action='ZoomPage' />"
275         "    <toolitem action='ZoomPageWidth' />"
276         "    <separator />"
277         "    <toolitem action='ZoomPrev' />"
278         "    <toolitem action='ZoomNext' />"
279         "  </toolbar>"
281         "  <toolbar name='StarToolbar'>"
282         "    <separator />"
283         "    <toolitem action='StarStateAction' />"
284         "    <separator />"
285         "    <toolitem action='FlatAction' />"
286         "    <separator />"
287         "    <toolitem action='MagnitudeAction' />"
288         "    <toolitem action='SpokeAction' />"
289         "    <toolitem action='RoundednessAction' />"
290         "    <toolitem action='RandomizationAction' />"
291         "    <separator />"
292         "    <toolitem action='StarResetAction' />"
293         "  </toolbar>"
295         "  <toolbar name='RectToolbar'>"
296         "    <toolitem action='RectStateAction' />"
297         "    <toolitem action='RectWidthAction' />"
298         "    <toolitem action='RectHeightAction' />"
299         "    <toolitem action='RadiusXAction' />"
300         "    <toolitem action='RadiusYAction' />"
301         "    <toolitem action='RectUnitsAction' />"
302         "    <separator />"
303         "    <toolitem action='RectResetAction' />"
304         "  </toolbar>"
306         "  <toolbar name='3DBoxToolbar'>"
307         "    <toolitem action='3DBoxPosAngleXAction' />"
308         "    <toolitem action='3DBoxVPXAction' />"
309         "    <separator />"
310         "    <toolitem action='3DBoxPosAngleYAction' />"
311         "    <toolitem action='3DBoxVPYAction' />"
312         "    <separator />"
313         "    <toolitem action='3DBoxPosAngleZAction' />"
314         "    <toolitem action='3DBoxVPZAction' />"
315         "    <separator />"
316         "  </toolbar>"
318         "  <toolbar name='SpiralToolbar'>"
319         "    <toolitem action='SpiralStateAction' />"
320         "    <toolitem action='SpiralRevolutionAction' />"
321         "    <toolitem action='SpiralExpansionAction' />"
322         "    <toolitem action='SpiralT0Action' />"
323         "    <separator />"
324         "    <toolitem action='SpiralResetAction' />"
325         "  </toolbar>"
327         "  <toolbar name='PenToolbar'>"
328         "  </toolbar>"
330         "  <toolbar name='PencilToolbar'>"
331         "  </toolbar>"
333         "  <toolbar name='CalligraphyToolbar'>"
334         "    <separator />"
335         "    <toolitem action='CalligraphyWidthAction' />"
336         "    <toolitem action='PressureAction' />"
337         "    <toolitem action='TraceAction' />"
338         "    <toolitem action='ThinningAction' />"
339         "    <separator />"
340         "    <toolitem action='AngleAction' />"
341         "    <toolitem action='TiltAction' />"
342         "    <toolitem action='FixationAction' />"
343         "    <separator />"
344         "    <toolitem action='CapRoundingAction' />"
345         "    <separator />"
346         "    <toolitem action='TremorAction' />"
347         "    <toolitem action='WiggleAction' />"
348         "    <toolitem action='MassAction' />"
349         "    <separator />"
350         "    <toolitem action='CalligraphyResetAction' />"
351         "  </toolbar>"
353         "  <toolbar name='ArcToolbar'>"
354         "    <toolitem action='ArcStateAction' />"
355         "    <separator />"
356         "    <toolitem action='ArcStartAction' />"
357         "    <toolitem action='ArcEndAction' />"
358         "    <separator />"
359         "    <toolitem action='ArcOpenAction' />"
360         "    <separator />"
361         "    <toolitem action='ArcResetAction' />"
362         "    <separator />"
363         "  </toolbar>"
365         "  <toolbar name='PaintbucketToolbar'>"
366         "    <toolitem action='ChannelsAction' />"
367         "    <separator />"
368         "    <toolitem action='ThresholdAction' />"
369         "    <separator />"
370         "    <toolitem action='OffsetAction' />"
371         "    <toolitem action='PaintbucketUnitsAction' />"
372         "    <separator />"
373         "    <toolitem action='AutoGapAction' />"
374         "    <separator />"
375         "    <toolitem action='PaintbucketResetAction' />"
376         "  </toolbar>"
378         "  <toolbar name='DropperToolbar'>"
379         "    <toolitem action='DropperPickAlphaAction' />"
380         "    <toolitem action='DropperSetAlphaAction' />"
381         "  </toolbar>"
383         "  <toolbar name='ConnectorToolbar'>"
384         "    <toolitem action='ConnectorAvoidAction' />"
385         "    <toolitem action='ConnectorIgnoreAction' />"
386         "    <toolitem action='ConnectorSpacingAction' />"
387         "    <toolitem action='ConnectorGraphAction' />"
388         "    <toolitem action='ConnectorLengthAction' />"
389         "    <toolitem action='ConnectorDirectedAction' />"
390         "    <toolitem action='ConnectorOverlapAction' />"
391         "  </toolbar>"
393         "</ui>"
396 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
398 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
400 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
401 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
403 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
404 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
406 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
407 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
409 /* Global text entry widgets necessary for update */
410 /* GtkWidget *dropper_rgb_entry,
411           *dropper_opacity_entry ; */
412 // should be made a private member once this is converted to class
414 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
415     connection->disconnect();
416     delete connection;
419 static void purge_repr_listener( GObject* obj, GObject* tbl )
421     (void)obj;
422     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
423     if (oldrepr) { // remove old listener
424         sp_repr_remove_listener_by_data(oldrepr, tbl);
425         Inkscape::GC::release(oldrepr);
426         oldrepr = 0;
427         g_object_set_data( tbl, "repr", NULL );
428     }
431 GtkWidget *
432 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
433                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
434                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
436     SPAction *action = verb->get_action(view);
437     if (!action) return NULL;
439     SPAction *doubleclick_action;
440     if (doubleclick_verb)
441         doubleclick_action = doubleclick_verb->get_action(view);
442     else
443         doubleclick_action = NULL;
445     /* fixme: Handle sensitive/unsensitive */
446     /* fixme: Implement sp_button_new_from_action */
447     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
448     gtk_widget_show(b);
449     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
451     return b;
454 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
455                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
457     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
460 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
461                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
463     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
467 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
469     SPAction* targetAction = SP_ACTION(user_data);
470     if ( targetAction ) {
471         sp_action_perform( targetAction, NULL );
472     }
475 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
477     if ( data ) {
478         GtkAction* act = GTK_ACTION(data);
479         gtk_action_set_sensitive( act, sensitive );
480     }
483 static SPActionEventVector action_event_vector = {
484     {NULL},
485     NULL,
486     NULL,
487     sp_action_action_set_sensitive,
488     NULL,
489     NULL
490 };
492 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
494     GtkAction* act = 0;
496     SPAction* targetAction = verb->get_action(view);
497     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
498     act = GTK_ACTION(inky);
499     gtk_action_set_sensitive( act, targetAction->sensitive );
501     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
503     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
504     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
506     return act;
509 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
511     Inkscape::UI::View::View *view = desktop;
512     gint verbsToUse[] = {
513         // disabled until we have icons for them:
514         //find
515         //SP_VERB_EDIT_TILE,
516         //SP_VERB_EDIT_UNTILE,
517         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
518         SP_VERB_DIALOG_DISPLAY,
519         SP_VERB_DIALOG_FILL_STROKE,
520         SP_VERB_DIALOG_NAMEDVIEW,
521         SP_VERB_DIALOG_TEXT,
522         SP_VERB_DIALOG_XML_EDITOR,
523         SP_VERB_EDIT_CLONE,
524         SP_VERB_EDIT_COPY,
525         SP_VERB_EDIT_CUT,
526         SP_VERB_EDIT_DUPLICATE,
527         SP_VERB_EDIT_PASTE,
528         SP_VERB_EDIT_REDO,
529         SP_VERB_EDIT_UNDO,
530         SP_VERB_EDIT_UNLINK_CLONE,
531         SP_VERB_FILE_EXPORT,
532         SP_VERB_FILE_IMPORT,
533         SP_VERB_FILE_NEW,
534         SP_VERB_FILE_OPEN,
535         SP_VERB_FILE_PRINT,
536         SP_VERB_FILE_SAVE,
537         SP_VERB_OBJECT_TO_CURVE,
538         SP_VERB_SELECTION_GROUP,
539         SP_VERB_SELECTION_OUTLINE,
540         SP_VERB_SELECTION_UNGROUP,
541         SP_VERB_ZOOM_1_1,
542         SP_VERB_ZOOM_1_2,
543         SP_VERB_ZOOM_2_1,
544         SP_VERB_ZOOM_DRAWING,
545         SP_VERB_ZOOM_IN,
546         SP_VERB_ZOOM_NEXT,
547         SP_VERB_ZOOM_OUT,
548         SP_VERB_ZOOM_PAGE,
549         SP_VERB_ZOOM_PAGE_WIDTH,
550         SP_VERB_ZOOM_PREV,
551         SP_VERB_ZOOM_SELECTION,
552     };
554     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
555     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
557     static std::map<SPDesktop*, GtkActionGroup*> groups;
558     GtkActionGroup* mainActions = 0;
559     if ( groups.find(desktop) != groups.end() ) {
560         mainActions = groups[desktop];
561     }
563     if ( !mainActions ) {
564         mainActions = gtk_action_group_new("main");
565         groups[desktop] = mainActions;
566     }
568     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
569         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
570         if ( verb ) {
571             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
572                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
573                 gtk_action_group_add_action( mainActions, act );
574             }
575         }
576     }
578     return mainActions;
582 GtkWidget *
583 sp_tool_toolbox_new()
585     GtkTooltips *tt = gtk_tooltips_new();
586     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
588     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
589     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
591     gtk_widget_set_sensitive(tb, FALSE);
593     GtkWidget *hb = gtk_handle_box_new();
594     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
595     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
596     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
598     gtk_container_add(GTK_CONTAINER(hb), tb);
599     gtk_widget_show(GTK_WIDGET(tb));
601     sigc::connection* conn = new sigc::connection;
602     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
604     return hb;
607 static void
608 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
610     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
611     gtk_widget_queue_resize(child);
614 static void
615 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
617     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
618     gtk_widget_queue_resize(child);
621 GtkWidget *
622 sp_aux_toolbox_new()
624     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
626     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
627     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
628     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
629     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
630     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
632     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
633     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
635     gtk_widget_set_sensitive(tb, FALSE);
637     GtkWidget *hb = gtk_handle_box_new();
638     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
639     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
640     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
642     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
643     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
645     gtk_container_add(GTK_CONTAINER(hb), tb);
646     gtk_widget_show(GTK_WIDGET(tb));
648     sigc::connection* conn = new sigc::connection;
649     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
651     return hb;
654 //####################################
655 //# Commands Bar
656 //####################################
658 GtkWidget *
659 sp_commands_toolbox_new()
661     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
663     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
664     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
665     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
666     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
667     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
669     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
670     gtk_widget_set_sensitive(tb, FALSE);
672     GtkWidget *hb = gtk_handle_box_new();
673     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
674     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
675     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
677     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
678     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
680     gtk_container_add(GTK_CONTAINER(hb), tb);
681     gtk_widget_show(GTK_WIDGET(tb));
683     sigc::connection* conn = new sigc::connection;
684     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
686     return hb;
690 //####################################
691 //# node editing callbacks
692 //####################################
694 /**
695  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
696  */
697 static ShapeEditor *get_current_shape_editor()
699     if (!SP_ACTIVE_DESKTOP) {
700         return NULL;
701     }
703     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
705     if (!SP_IS_NODE_CONTEXT(event_context)) {
706         return NULL;
707     }
709     return SP_NODE_CONTEXT(event_context)->shape_editor;
713 void
714 sp_node_path_edit_add(void)
716     ShapeEditor *shape_editor = get_current_shape_editor();
717     if (shape_editor) shape_editor->add_node();
720 void
721 sp_node_path_edit_delete(void)
723     ShapeEditor *shape_editor = get_current_shape_editor();
724     if (shape_editor) shape_editor->delete_nodes();
727 void
728 sp_node_path_edit_delete_segment(void)
730     ShapeEditor *shape_editor = get_current_shape_editor();
731     if (shape_editor) shape_editor->delete_segment();
734 void
735 sp_node_path_edit_break(void)
737     ShapeEditor *shape_editor = get_current_shape_editor();
738     if (shape_editor) shape_editor->break_at_nodes();
741 void
742 sp_node_path_edit_join(void)
744     ShapeEditor *shape_editor = get_current_shape_editor();
745     if (shape_editor) shape_editor->join_nodes();
748 void
749 sp_node_path_edit_join_segment(void)
751     ShapeEditor *shape_editor = get_current_shape_editor();
752     if (shape_editor) shape_editor->join_segments();
755 void
756 sp_node_path_edit_toline(void)
758     ShapeEditor *shape_editor = get_current_shape_editor();
759     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
762 void
763 sp_node_path_edit_tocurve(void)
765     ShapeEditor *shape_editor = get_current_shape_editor();
766     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
769 void
770 sp_node_path_edit_cusp(void)
772     ShapeEditor *shape_editor = get_current_shape_editor();
773     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
776 void
777 sp_node_path_edit_smooth(void)
779     ShapeEditor *shape_editor = get_current_shape_editor();
780     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
783 void
784 sp_node_path_edit_symmetrical(void)
786     ShapeEditor *shape_editor = get_current_shape_editor();
787     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
790 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
791     bool show = gtk_toggle_action_get_active( act );
792     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
793     ShapeEditor *shape_editor = get_current_shape_editor();
794     if (shape_editor) shape_editor->show_handles(show);
797 //################################
798 //##    Node Editing Toolbox    ##
799 //################################
801 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* /*holder*/)
803     {
804         InkAction* inky = ink_action_new( "NodeInsertAction",
805                                           _("Insert node"),
806                                           _("Insert new nodes into selected segments"),
807                                           "node_insert",
808                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
809         g_object_set( inky, "short_label", _("Insert"), NULL );
810         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
811         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
812     }
814     {
815         InkAction* inky = ink_action_new( "NodeDeleteAction",
816                                           _("Delete node"),
817                                           _("Delete selected nodes"),
818                                           "node_delete",
819                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
820         g_object_set( inky, "short_label", _("Delete"), NULL );
821         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
822         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
823     }
825     {
826         InkAction* inky = ink_action_new( "NodeJoinAction",
827                                           _("Join endnodes"),
828                                           _("Join selected endnodes"),
829                                           "node_join",
830                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
831         g_object_set( inky, "short_label", _("Join"), NULL );
832         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
833         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
834     }
836     {
837         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
838                                           _("Join Segment"),
839                                           _("Join selected endnodes with a new segment"),
840                                           "node_join_segment",
841                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
842         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
843         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
844     }
846     {
847         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
848                                           _("Delete Segment"),
849                                           _("Split path between two non-endpoint nodes"),
850                                           "node_delete_segment",
851                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
852         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
853         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
854     }
856     {
857         InkAction* inky = ink_action_new( "NodeBreakAction",
858                                           _("Node Break"),
859                                           _("Break path at selected nodes"),
860                                           "node_break",
861                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
862         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
863         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
864     }
866     {
867         InkAction* inky = ink_action_new( "NodeCuspAction",
868                                           _("Node Cusp"),
869                                           _("Make selected nodes corner"),
870                                           "node_cusp",
871                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
872         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
873         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
874     }
876     {
877         InkAction* inky = ink_action_new( "NodeSmoothAction",
878                                           _("Node Smooth"),
879                                           _("Make selected nodes smooth"),
880                                           "node_smooth",
881                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
882         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
883         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
884     }
886     {
887         InkAction* inky = ink_action_new( "NodeSymmetricAction",
888                                           _("Node Symmetric"),
889                                           _("Make selected nodes symmetric"),
890                                           "node_symmetric",
891                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
892         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
893         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
894     }
896     {
897         InkAction* inky = ink_action_new( "NodeLineAction",
898                                           _("Node Line"),
899                                           _("Make selected segments lines"),
900                                           "node_line",
901                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
902         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
903         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
904     }
906     {
907         InkAction* inky = ink_action_new( "NodeCurveAction",
908                                           _("Node Curve"),
909                                           _("Make selected segments curves"),
910                                           "node_curve",
911                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
912         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
913         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
914     }
916     {
917         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
918                                                       _("Show Handles"),
919                                                       _("Show the Bezier handles of selected nodes"),
920                                                       "nodes_show_handles",
921                                                       Inkscape::ICON_SIZE_DECORATION );
922         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
923         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
924         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
925     }
927 } // end of sp_node_toolbox_prep()
930 //########################
931 //##    Zoom Toolbox    ##
932 //########################
934 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
936     // no custom GtkAction setup needed
937 } // end of sp_zoom_toolbox_prep()
939 void
940 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
942     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")));
946 void
947 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
949     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")));
952 void
953 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
955     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")));
958 static void
959 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
961     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
962     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
964     if (old_desktop) {
965         GList *children, *iter;
967         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
968         for ( iter = children ; iter ; iter = iter->next ) {
969             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
970         }
971         g_list_free(children);
972     }
974     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
976     if (desktop) {
977         gtk_widget_set_sensitive(toolbox, TRUE);
978         setup_func(toolbox, desktop);
979         update_func(desktop, desktop->event_context, toolbox);
980         *conn = desktop->connectEventContextChanged
981             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
982     } else {
983         gtk_widget_set_sensitive(toolbox, FALSE);
984     }
986 } // end of toolbox_set_desktop()
989 static void
990 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
992     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
993     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
994     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
995         // "toolbox.tools" was not set. Fallback to older value
996         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
998         // Copy the setting forwards
999         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1000     }
1001     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1003     for (int i = 0 ; tools[i].type_name ; i++ ) {
1004         GtkWidget *button =
1005             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1006                                                               SP_BUTTON_TYPE_TOGGLE,
1007                                                               Inkscape::Verb::get(tools[i].verb),
1008                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1009                                                               desktop,
1010                                                               tooltips );
1012         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1013                            (gpointer)button );
1014     }
1018 static void
1019 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1021     gchar const *const tname = ( eventcontext
1022                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1023                                  : NULL );
1024     for (int i = 0 ; tools[i].type_name ; i++ ) {
1025         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1026         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1027     }
1030 static void
1031 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1033     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1034     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1035     GtkUIManager* mgr = gtk_ui_manager_new();
1036     GError* errVal = 0;
1037     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1038     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1040     std::map<std::string, GtkWidget*> dataHolders;
1042     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1043         if ( aux_toolboxes[i].prep_func ) {
1044             // converted to GtkActions and UIManager
1046             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1047             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1048             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1049             dataHolders[aux_toolboxes[i].type_name] = kludge;
1050             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1051         } else {
1053             GtkWidget *sub_toolbox = 0;
1054             if (aux_toolboxes[i].create_func == NULL)
1055                 sub_toolbox = sp_empty_toolbox_new(desktop);
1056             else {
1057                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1058             }
1060             gtk_size_group_add_widget( grouper, sub_toolbox );
1062             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1063             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1065         }
1066     }
1068     // Second pass to create toolbars *after* all GtkActions are created
1069     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1070         if ( aux_toolboxes[i].prep_func ) {
1071             // converted to GtkActions and UIManager
1073             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1075             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1076             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1078             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1079             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1080             g_free( tmp );
1081             tmp = 0;
1083             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1084             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1085             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1086                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1087             }
1088             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1091             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1093             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1094                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1095                 swatch->setDesktop( desktop );
1096                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1097                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1098                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1099                 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 );
1100             }
1102             gtk_widget_show_all( holder );
1103             sp_set_font_size_smaller( holder );
1105             gtk_size_group_add_widget( grouper, holder );
1107             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1108             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1109         }
1110     }
1112     g_object_unref( G_OBJECT(grouper) );
1115 static void
1116 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1118     gchar const *tname = ( eventcontext
1119                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1120                            : NULL );
1121     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1122         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1123         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1124             gtk_widget_show_all(sub_toolbox);
1125             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1126         } else {
1127             gtk_widget_hide(sub_toolbox);
1128         }
1129     }
1132 static void
1133 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1135     gchar const * descr =
1136         "<ui>"
1137         "  <toolbar name='CommandsToolbar'>"
1138         "    <toolitem action='FileNew' />"
1139         "    <toolitem action='FileOpen' />"
1140         "    <toolitem action='FileSave' />"
1141         "    <toolitem action='FilePrint' />"
1142         "    <separator />"
1143         "    <toolitem action='FileImport' />"
1144         "    <toolitem action='FileExport' />"
1145         "    <separator />"
1146         "    <toolitem action='EditUndo' />"
1147         "    <toolitem action='EditRedo' />"
1148         "    <separator />"
1149         "    <toolitem action='EditCopy' />"
1150         "    <toolitem action='EditCut' />"
1151         "    <toolitem action='EditPaste' />"
1152         "    <separator />"
1153         "    <toolitem action='ZoomSelection' />"
1154         "    <toolitem action='ZoomDrawing' />"
1155         "    <toolitem action='ZoomPage' />"
1156         "    <separator />"
1157         "    <toolitem action='EditDuplicate' />"
1158         "    <toolitem action='EditClone' />"
1159         "    <toolitem action='EditUnlinkClone' />"
1160         "    <separator />"
1161         "    <toolitem action='SelectionGroup' />"
1162         "    <toolitem action='SelectionUnGroup' />"
1163         "    <separator />"
1164         "    <toolitem action='DialogFillStroke' />"
1165         "    <toolitem action='DialogText' />"
1166         "    <toolitem action='DialogXMLEditor' />"
1167         "    <toolitem action='DialogAlignDistribute' />"
1168         "    <separator />"
1169         "    <toolitem action='DialogPreferences' />"
1170         "    <toolitem action='DialogDocumentProperties' />"
1171         "  </toolbar>"
1172         "</ui>";
1173     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1176     GtkUIManager* mgr = gtk_ui_manager_new();
1177     GError* errVal = 0;
1179     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1180     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1182     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1183     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1184         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1185     }
1186     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1187     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1188     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1191     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1194 static void
1195 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1199 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1201     gtk_widget_show(toolbox_toplevel);
1202     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1204     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1205     if (!shown_toolbox) {
1206         return;
1207     }
1208     gtk_widget_show(toolbox);
1210     // need to show the spacer, or the padding will be off
1211     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1212     gtk_widget_show(spacer);
1214     gtk_widget_show_all(shown_toolbox);
1217 void
1218 aux_toolbox_space(GtkWidget *tb, gint space)
1220     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1223 static GtkWidget *
1224 sp_empty_toolbox_new(SPDesktop *desktop)
1226     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1227     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1228     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1230     gtk_widget_show_all(tbl);
1231     sp_set_font_size_smaller (tbl);
1233     return tbl;
1236 // helper UI functions
1238 GtkWidget *
1239 sp_tb_spinbutton(
1240     gchar *label, gchar const *tooltip,
1241     gchar const *path, gchar const *data, gdouble def,
1242     GtkWidget *us,
1243     GtkWidget *tbl,
1244     gboolean altx, gchar const *altx_mark,
1245     gdouble lower, gdouble upper, gdouble step, gdouble page,
1246     void (*callback)(GtkAdjustment *, GtkWidget *),
1247     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1249     GtkTooltips *tt = gtk_tooltips_new();
1251     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1253     GtkWidget *l = gtk_label_new(label);
1254     gtk_widget_show(l);
1255     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1256     gtk_container_add(GTK_CONTAINER(hb), l);
1258     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1259                                       lower, upper, step, page, page);
1260     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1261     if (us)
1262         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1264     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1265     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1266     if (altx)
1267         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1268     gtk_widget_set_size_request(sb,
1269                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1270                                 AUX_SPINBUTTON_HEIGHT);
1271     gtk_widget_show(sb);
1272     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1273     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1274     gtk_container_add(GTK_CONTAINER(hb), sb);
1275     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1277     return hb;
1280 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1281                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
1282                                                        gchar const *path, gchar const *data, gdouble def,
1283                                                        GtkWidget *focusTarget,
1284                                                        GtkWidget *us,
1285                                                        GObject *dataKludge,
1286                                                        gboolean altx, gchar const *altx_mark,
1287                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1288                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1289                                                        void (*callback)(GtkAdjustment *, GObject *),
1290                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1292     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
1293                                                              lower, upper, step, page, page ) );
1294     if (us) {
1295         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1296     }
1298     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1300     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1301     if ( shortLabel ) {
1302         g_object_set( act, "short_label", shortLabel, NULL );
1303     }
1305     if ( (descrCount > 0) && descrLabels && descrValues ) {
1306         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1307     }
1309     if ( focusTarget ) {
1310         ege_adjustment_action_set_focuswidget( act, focusTarget );
1311     }
1313     if ( altx && altx_mark ) {
1314         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1315     }
1317     if ( dataKludge ) {
1318         g_object_set_data( dataKludge, data, adj );
1319     }
1321     // Using a cast just to make sure we pass in the right kind of function pointer
1322     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1324     return act;
1328 #define MODE_LABEL_WIDTH 70
1330 //########################
1331 //##       Star         ##
1332 //########################
1334 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1336     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1338     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1339         // do not remember prefs if this call is initiated by an undo change, because undoing object
1340         // creation sets bogus values to its attributes before it is deleted
1341         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1342     }
1344     // quit if run by the attr_changed listener
1345     if (g_object_get_data( dataKludge, "freeze" )) {
1346         return;
1347     }
1349     // in turn, prevent listener from responding
1350     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1352     bool modmade = false;
1354     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1355     GSList const *items = selection->itemList();
1356     for (; items != NULL; items = items->next) {
1357         if (SP_IS_STAR((SPItem *) items->data)) {
1358             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1359             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1360             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1361                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1362                                     + M_PI / (gint)adj->value));
1363             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1364             modmade = true;
1365         }
1366     }
1367     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1368                                    _("Star: Change number of corners"));
1370     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1373 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1375     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1377     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1378         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1379     }
1381     // quit if run by the attr_changed listener
1382     if (g_object_get_data( dataKludge, "freeze" )) {
1383         return;
1384     }
1386     // in turn, prevent listener from responding
1387     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1389     bool modmade = false;
1390     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1391     GSList const *items = selection->itemList();
1392     for (; items != NULL; items = items->next) {
1393         if (SP_IS_STAR((SPItem *) items->data)) {
1394             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1396             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1397             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1398             if (r2 < r1) {
1399                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1400             } else {
1401                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1402             }
1404             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1405             modmade = true;
1406         }
1407     }
1409     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1410                                    _("Star: Change spoke ratio"));
1412     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1415 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1417     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1418     bool flat = ege_select_one_action_get_active( act ) == 0;
1420     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1421         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1422                                     flat ? "true" : "false" );
1423     }
1425     // quit if run by the attr_changed listener
1426     if (g_object_get_data( dataKludge, "freeze" )) {
1427         return;
1428     }
1430     // in turn, prevent listener from responding
1431     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1433     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1434     GSList const *items = selection->itemList();
1435     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1436     bool modmade = false;
1438     if ( prop_action ) {
1439         gtk_action_set_sensitive( prop_action, !flat );
1440     }
1442     for (; items != NULL; items = items->next) {
1443         if (SP_IS_STAR((SPItem *) items->data)) {
1444             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1445             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1446             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1447             modmade = true;
1448         }
1449     }
1451     if (modmade) {
1452         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1453                          flat ? _("Make polygon") : _("Make star"));
1454     }
1456     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1459 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1461     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1463     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1464         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1465     }
1467     // quit if run by the attr_changed listener
1468     if (g_object_get_data( dataKludge, "freeze" )) {
1469         return;
1470     }
1472     // in turn, prevent listener from responding
1473     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1475     bool modmade = false;
1477     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1478     GSList const *items = selection->itemList();
1479     for (; items != NULL; items = items->next) {
1480         if (SP_IS_STAR((SPItem *) items->data)) {
1481             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1482             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1483             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1484             modmade = true;
1485         }
1486     }
1487     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1488                                    _("Star: Change rounding"));
1490     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1493 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1495     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1497     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1498         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1499     }
1501     // quit if run by the attr_changed listener
1502     if (g_object_get_data( dataKludge, "freeze" )) {
1503         return;
1504     }
1506     // in turn, prevent listener from responding
1507     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1509     bool modmade = false;
1511     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1512     GSList const *items = selection->itemList();
1513     for (; items != NULL; items = items->next) {
1514         if (SP_IS_STAR((SPItem *) items->data)) {
1515             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1516             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1517             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1518             modmade = true;
1519         }
1520     }
1521     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1522                                    _("Star: Change randomization"));
1524     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1528 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1529                                        gchar const */*old_value*/, gchar const */*new_value*/,
1530                                        bool /*is_interactive*/, gpointer data)
1532     GtkWidget *tbl = GTK_WIDGET(data);
1534     // quit if run by the _changed callbacks
1535     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1536         return;
1537     }
1539     // in turn, prevent callbacks from responding
1540     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1542     GtkAdjustment *adj = 0;
1544     if (!strcmp(name, "inkscape:randomized")) {
1545         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1546         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1547     } else if (!strcmp(name, "inkscape:rounded")) {
1548         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1549         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1550     } else if (!strcmp(name, "inkscape:flatsided")) {
1551         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1552         char const *flatsides = repr->attribute("inkscape:flatsided");
1553         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1554         if ( flatsides && !strcmp(flatsides,"false") ) {
1555             ege_select_one_action_set_active( flat_action, 1 );
1556             gtk_action_set_sensitive( prop_action, TRUE );
1557         } else {
1558             ege_select_one_action_set_active( flat_action, 0 );
1559             gtk_action_set_sensitive( prop_action, FALSE );
1560         }
1561     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1562         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1563         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1564         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1565         if (r2 < r1) {
1566             gtk_adjustment_set_value(adj, r2/r1);
1567         } else {
1568             gtk_adjustment_set_value(adj, r1/r2);
1569         }
1570     } else if (!strcmp(name, "sodipodi:sides")) {
1571         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1572         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1573     }
1575     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1579 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1581     NULL, /* child_added */
1582     NULL, /* child_removed */
1583     star_tb_event_attr_changed,
1584     NULL, /* content_changed */
1585     NULL  /* order_changed */
1586 };
1589 /**
1590  *  \param selection Should not be NULL.
1591  */
1592 static void
1593 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1595     int n_selected = 0;
1596     Inkscape::XML::Node *repr = NULL;
1598     purge_repr_listener( tbl, tbl );
1600     for (GSList const *items = selection->itemList();
1601          items != NULL;
1602          items = items->next)
1603     {
1604         if (SP_IS_STAR((SPItem *) items->data)) {
1605             n_selected++;
1606             repr = SP_OBJECT_REPR((SPItem *) items->data);
1607         }
1608     }
1610     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1612     if (n_selected == 0) {
1613         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1614     } else if (n_selected == 1) {
1615         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1617         if (repr) {
1618             g_object_set_data( tbl, "repr", repr );
1619             Inkscape::GC::anchor(repr);
1620             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1621             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1622         }
1623     } else {
1624         // FIXME: implement averaging of all parameters for multiple selected stars
1625         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1626         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1627     }
1631 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1633     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1634     // callbacks to lump all the changes for all selected objects in one undo step
1636     GtkAdjustment *adj = 0;
1638     // fixme: make settable in prefs!
1639     gint mag = 5;
1640     gdouble prop = 0.5;
1641     gboolean flat = FALSE;
1642     gdouble randomized = 0;
1643     gdouble rounded = 0;
1645     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1646     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1648     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1649     gtk_action_set_sensitive( sb2, !flat );
1651     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1652     gtk_adjustment_set_value(adj, mag);
1653     gtk_adjustment_value_changed(adj);
1655     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1656     gtk_adjustment_set_value(adj, prop);
1657     gtk_adjustment_value_changed(adj);
1659     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1660     gtk_adjustment_set_value(adj, rounded);
1661     gtk_adjustment_value_changed(adj);
1663     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1664     gtk_adjustment_set_value(adj, randomized);
1665     gtk_adjustment_value_changed(adj);
1669 void
1670 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1672     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1673     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1674     GtkWidget *l = gtk_label_new(NULL);
1675     gtk_label_set_markup(GTK_LABEL(l), title);
1676     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1677     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1678     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1682 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1684     {
1685         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1686         ege_output_action_set_use_markup( act, TRUE );
1687         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1688         g_object_set_data( holder, "mode_action", act );
1689     }
1691     {
1692         //EgeAdjustmentAction* calligraphy_angle = 0;
1693         EgeAdjustmentAction* eact = 0;
1694         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1695         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1697         /* Flatsided checkbox */
1698         {
1699             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1701             GtkTreeIter iter;
1702             gtk_list_store_append( model, &iter );
1703             gtk_list_store_set( model, &iter,
1704                                 0, _("Polygon"),
1705                                 1, _("Regular polygon (with one handle) instead of a star"),
1706                                 2, "star_flat",
1707                                 -1 );
1709             gtk_list_store_append( model, &iter );
1710             gtk_list_store_set( model, &iter,
1711                                 0, _("Star"),
1712                                 1, _("Star instead of a regular polygon (with one handle)"),
1713                                 2, "star_angled",
1714                                 -1 );
1716             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1717             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1718             g_object_set_data( holder, "flat_action", act );
1720             ege_select_one_action_set_appearance( act, "full" );
1721             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1722             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1723             ege_select_one_action_set_icon_column( act, 2 );
1724             ege_select_one_action_set_tooltip_column( act, 1  );
1726             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1727             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1728         }
1730         /* Magnitude */
1731         {
1732         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1733         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1734         eact = create_adjustment_action( "MagnitudeAction",
1735                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1736                                          "tools.shapes.star", "magnitude", 3,
1737                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1738                                          3, 1024, 1, 5,
1739                                          labels, values, G_N_ELEMENTS(labels),
1740                                          sp_stb_magnitude_value_changed,
1741                                          1.0, 0 );
1742         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1743         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1744         }
1746         /* Spoke ratio */
1747         {
1748         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1749         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1750         eact = create_adjustment_action( "SpokeAction",
1751                                          _("Spoke ratio"), _("Spoke ratio:"),
1752                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1753                                          // Base radius is the same for the closest handle.
1754                                          _("Base radius to tip radius ratio"),
1755                                          "tools.shapes.star", "proportion", 0.5,
1756                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1757                                          0.01, 1.0, 0.01, 0.1,
1758                                          labels, values, G_N_ELEMENTS(labels),
1759                                          sp_stb_proportion_value_changed );
1760         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1761         g_object_set_data( holder, "prop_action", eact );
1762         }
1764         if ( !isFlatSided ) {
1765             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1766         } else {
1767             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1768         }
1770         /* Roundedness */
1771         {
1772         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1773         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1774         eact = create_adjustment_action( "RoundednessAction",
1775                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1776                                          "tools.shapes.star", "rounded", 0.0,
1777                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1778                                          -10.0, 10.0, 0.01, 0.1,
1779                                          labels, values, G_N_ELEMENTS(labels),
1780                                          sp_stb_rounded_value_changed );
1781         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1782         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1783         }
1785         /* Randomization */
1786         {
1787         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1788         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1789         eact = create_adjustment_action( "RandomizationAction",
1790                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1791                                          "tools.shapes.star", "randomized", 0.0,
1792                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1793                                          -10.0, 10.0, 0.001, 0.01,
1794                                          labels, values, G_N_ELEMENTS(labels),
1795                                          sp_stb_randomized_value_changed, 0.1, 3 );
1796         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1797         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1798         }
1799     }
1801     {
1802         /* Reset */
1803         {
1804             GtkAction* act = gtk_action_new( "StarResetAction",
1805                                              _("Defaults"),
1806                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1807                                              GTK_STOCK_CLEAR );
1808             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1809             gtk_action_group_add_action( mainActions, act );
1810             gtk_action_set_sensitive( act, TRUE );
1811         }
1812     }
1814     sigc::connection *connection = new sigc::connection(
1815         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1816         );
1817     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1818     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1822 //########################
1823 //##       Rect         ##
1824 //########################
1826 static void sp_rtb_sensitivize( GObject *tbl )
1828     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1829     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1830     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1832     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1833         gtk_action_set_sensitive( not_rounded, FALSE );
1834     } else {
1835         gtk_action_set_sensitive( not_rounded, TRUE );
1836     }
1840 static void
1841 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1842                           void (*setter)(SPRect *, gdouble))
1844     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1846     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1847     SPUnit const *unit = tracker->getActiveUnit();
1849     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1850         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1851     }
1853     // quit if run by the attr_changed listener
1854     if (g_object_get_data( tbl, "freeze" )) {
1855         return;
1856     }
1858     // in turn, prevent listener from responding
1859     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1861     bool modmade = false;
1862     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1863     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1864         if (SP_IS_RECT(items->data)) {
1865             if (adj->value != 0) {
1866                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
1867             } else {
1868                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
1869             }
1870             modmade = true;
1871         }
1872     }
1874     sp_rtb_sensitivize( tbl );
1876     if (modmade) {
1877         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
1878                                    _("Change rectangle"));
1879     }
1881     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1884 static void
1885 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
1887     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
1890 static void
1891 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
1893     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
1896 static void
1897 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
1899     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
1902 static void
1903 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
1905     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
1910 static void
1911 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
1913     GtkAdjustment *adj = 0;
1915     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
1916     gtk_adjustment_set_value(adj, 0.0);
1917     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
1918     gtk_adjustment_value_changed(adj);
1920     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
1921     gtk_adjustment_set_value(adj, 0.0);
1922     gtk_adjustment_value_changed(adj);
1924     sp_rtb_sensitivize( obj );
1927 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
1928                                        gchar const */*old_value*/, gchar const */*new_value*/,
1929                                        bool /*is_interactive*/, gpointer data)
1931     GObject *tbl = G_OBJECT(data);
1933     // quit if run by the _changed callbacks
1934     if (g_object_get_data( tbl, "freeze" )) {
1935         return;
1936     }
1938     // in turn, prevent callbacks from responding
1939     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
1941     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1942     SPUnit const *unit = tracker->getActiveUnit();
1944     gpointer item = g_object_get_data( tbl, "item" );
1945     if (item && SP_IS_RECT(item)) {
1946         {
1947             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
1948             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
1949             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
1950         }
1952         {
1953             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
1954             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
1955             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
1956         }
1958         {
1959             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
1960             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
1961             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
1962         }
1964         {
1965             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
1966             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
1967             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
1968         }
1969     }
1971     sp_rtb_sensitivize( tbl );
1973     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1977 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
1978     NULL, /* child_added */
1979     NULL, /* child_removed */
1980     rect_tb_event_attr_changed,
1981     NULL, /* content_changed */
1982     NULL  /* order_changed */
1983 };
1985 /**
1986  *  \param selection should not be NULL.
1987  */
1988 static void
1989 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1991     int n_selected = 0;
1992     Inkscape::XML::Node *repr = NULL;
1993     SPItem *item = NULL;
1995     if ( g_object_get_data( tbl, "repr" ) ) {
1996         g_object_set_data( tbl, "item", NULL );
1997     }
1998     purge_repr_listener( tbl, tbl );
2000     for (GSList const *items = selection->itemList();
2001          items != NULL;
2002          items = items->next) {
2003         if (SP_IS_RECT((SPItem *) items->data)) {
2004             n_selected++;
2005             item = (SPItem *) items->data;
2006             repr = SP_OBJECT_REPR(item);
2007         }
2008     }
2010     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2012     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2014     if (n_selected == 0) {
2015         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2017         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2018         gtk_action_set_sensitive(w, FALSE);
2019         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2020         gtk_action_set_sensitive(h, FALSE);
2022     } else if (n_selected == 1) {
2023         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2024         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2026         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2027         gtk_action_set_sensitive(w, TRUE);
2028         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2029         gtk_action_set_sensitive(h, TRUE);
2031         if (repr) {
2032             g_object_set_data( tbl, "repr", repr );
2033             g_object_set_data( tbl, "item", item );
2034             Inkscape::GC::anchor(repr);
2035             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2036             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2037         }
2038     } else {
2039         // FIXME: implement averaging of all parameters for multiple selected
2040         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2041         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2042         sp_rtb_sensitivize( tbl );
2043     }
2047 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2049     EgeAdjustmentAction* eact = 0;
2051     {
2052         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2053         ege_output_action_set_use_markup( act, TRUE );
2054         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2055         g_object_set_data( holder, "mode_action", act );
2056     }
2058     // rx/ry units menu: create
2059     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2060     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2061     // fixme: add % meaning per cent of the width/height
2062     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2063     g_object_set_data( holder, "tracker", tracker );
2065     /* W */
2066     {
2067         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2068         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2069         eact = create_adjustment_action( "RectWidthAction",
2070                                          _("Width"), _("W:"), _("Width of rectangle"),
2071                                          "tools.shapes.rect", "width", 0,
2072                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2073                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2074                                          labels, values, G_N_ELEMENTS(labels),
2075                                          sp_rtb_width_value_changed );
2076         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2077         g_object_set_data( holder, "width_action", eact );
2078         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2079         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2080     }
2082     /* H */
2083     {
2084         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2085         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2086         eact = create_adjustment_action( "RectHeightAction",
2087                                          _("Height"), _("H:"), _("Height of rectangle"),
2088                                          "tools.shapes.rect", "height", 0,
2089                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2090                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2091                                          labels, values, G_N_ELEMENTS(labels),
2092                                          sp_rtb_height_value_changed );
2093         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2094         g_object_set_data( holder, "height_action", eact );
2095         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2096         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2097     }
2099     /* rx */
2100     {
2101         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2102         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2103         eact = create_adjustment_action( "RadiusXAction",
2104                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2105                                          "tools.shapes.rect", "rx", 0,
2106                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2107                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2108                                          labels, values, G_N_ELEMENTS(labels),
2109                                          sp_rtb_rx_value_changed);
2110         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2111         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2112     }
2114     /* ry */
2115     {
2116         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2117         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2118         eact = create_adjustment_action( "RadiusYAction",
2119                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2120                                          "tools.shapes.rect", "ry", 0,
2121                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2122                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2123                                          labels, values, G_N_ELEMENTS(labels),
2124                                          sp_rtb_ry_value_changed);
2125         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2126         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2127     }
2129     // add the units menu
2130     {
2131         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2132         gtk_action_group_add_action( mainActions, act );
2133     }
2135     /* Reset */
2136     {
2137         InkAction* inky = ink_action_new( "RectResetAction",
2138                                           _("Not rounded"),
2139                                           _("Make corners sharp"),
2140                                           "squared_corner",
2141                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2142         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2143         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2144         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2145         g_object_set_data( holder, "not_rounded", inky );
2146     }
2148     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2149     sp_rtb_sensitivize( holder );
2151     sigc::connection *connection = new sigc::connection(
2152         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2153         );
2154     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2155     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2158 //########################
2159 //##       3D Box       ##
2160 //########################
2162 static void sp_3dbox_toggle_vp_changed (GtkToggleAction */*act*/, GObject *dataKludge, Box3D::Axis axis)
2164     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2165     SPDocument *document = sp_desktop_document (desktop);
2166     Box3D::Perspective3D *persp = document->current_perspective;
2168     g_return_if_fail (is_single_axis_direction (axis));
2169     g_return_if_fail (persp);
2171     persp->toggle_boxes (axis);
2173     gchar *str;
2174     switch (axis) {
2175         case Box3D::X:
2176             str = g_strdup ("box3d_angle_x_action");
2177             break;
2178         case Box3D::Y:
2179             str = g_strdup ("box3d_angle_y_action");
2180             break;
2181         case Box3D::Z:
2182             str = g_strdup ("box3d_angle_z_action");
2183             break;
2184         default:
2185             return;
2186     }
2187     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2188     if (angle_action) {
2189         gtk_action_set_sensitive (angle_action, !persp->get_vanishing_point (axis)->is_finite() );
2190     }
2192     // FIXME: Given how it is realized in the other tools, this is probably not the right way to do it,
2193     //        but without the if construct, we get continuous segfaults. Needs further investigation.
2194     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2195         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
2196                          _("3D Box: Change perspective"));
2197     }
2200 static void sp_3dbox_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2202     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::X);
2205 static void sp_3dbox_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2207     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Y);
2210 static void sp_3dbox_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2212     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Z);
2215 static void sp_3dbox_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Box3D::Axis axis )
2217     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2218     Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2220     if (persp) {
2221         double angle = adj->value * M_PI/180;
2222         persp->set_infinite_direction (axis, NR::Point (cos (angle), sin (angle)));
2224         // FIXME: See comment above; without the if construct we get segfaults during undo.
2225         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2226             sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2227                              _("3D Box: Change perspective"));
2228         }
2229     }
2230     //g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
2233 static void sp_3dbox_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2235     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::X);
2238 static void sp_3dbox_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2240     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Y);
2243 static void sp_3dbox_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2245     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Z);
2248 // normalize angle so that it lies in the interval [0,360]
2249 static double sp_3dbox_normalize_angle (double a) {
2250     double angle = a + ((int) (a/360.0))*360;
2251     if (angle < 0) {
2252         angle += 360.0;
2253     }
2254     return angle;
2257 static void sp_3dbox_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *name,
2258                                            gchar const */*old_value*/, gchar const */*new_value*/,
2259                                            bool /*is_interactive*/, gpointer data)
2261     GtkWidget *tbl = GTK_WIDGET(data);
2263     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2264     /***
2265     // quit if run by the _changed callbacks
2266     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2267         return;
2268     }
2270     // in turn, prevent callbacks from responding
2271     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2272     ***/
2274     if (!strcmp(name, "inkscape:perspective")) {
2275         GtkAdjustment *adj = 0;
2276         double angle;
2277         SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2278         Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2280         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2281         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::X)->get_angle());
2282         gtk_adjustment_set_value(adj, angle);
2284         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2285         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Y)->get_angle());
2286         gtk_adjustment_set_value(adj, angle);
2288         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2289         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Z)->get_angle());
2290         gtk_adjustment_set_value(adj, angle);
2291     }
2294 static Inkscape::XML::NodeEventVector sp_3dbox_tb_repr_events =
2296     NULL, /* child_added */
2297     NULL, /* child_removed */
2298     sp_3dbox_tb_event_attr_changed,
2299     NULL, /* content_changed */
2300     NULL  /* order_changed */
2301 };
2303 /**
2304  *  \param selection Should not be NULL.
2305  */
2306 static void
2307 sp_3dbox_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2309     Inkscape::XML::Node *repr = NULL;
2310     purge_repr_listener(tbl, tbl);
2312     SPItem *item = selection->singleItem();
2313     if (item) {
2314         repr = SP_OBJECT_REPR(item);
2315         if (repr) {
2316             g_object_set_data(tbl, "repr", repr);
2317             Inkscape::GC::anchor(repr);
2318             sp_repr_add_listener(repr, &sp_3dbox_tb_repr_events, tbl);
2319             sp_repr_synthesize_events(repr, &sp_3dbox_tb_repr_events, tbl);
2320         }
2321     }
2324 static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2326     EgeAdjustmentAction* eact = 0;
2327     SPDocument *document = sp_desktop_document (desktop);
2328     Box3D::Perspective3D *persp = document->current_perspective;
2329     bool toggled = false;
2331     /* angle of VP in X direction */
2332     eact = create_adjustment_action("3DBoxPosAngleXAction",
2333                                     _("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2334                                     "tools.shapes.3dbox", "dir_vp_x", persp->get_vanishing_point (Box3D::X)->get_angle(),
2335                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2336                                     0.0, 360.0, 1.0, 10.0,
2337                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2338                                     sp_3dbox_vpx_angle_changed,
2339                                     0.1, 1);
2340     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2341     g_object_set_data(holder, "box3d_angle_x_action", eact);
2342     if (!persp->get_vanishing_point (Box3D::X)->is_finite()) {
2343         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2344     } else {
2345         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2346     }
2348     /* toggle VP in X direction */
2349     {
2350     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2351                                                   _("Toggle VP in X direction"),
2352                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2353                                                   "toggle_vp_x",
2354                                                   Inkscape::ICON_SIZE_DECORATION);
2355     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2356     if (persp) {
2357         toggled = !persp->get_vanishing_point(Box3D::X)->is_finite();
2358     }
2359     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2360     /* we connect the signal after setting the state to avoid switching the state again */
2361     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_x_changed), holder);
2362     }
2364     /* angle of VP in Y direction */
2365     eact = create_adjustment_action("3DBoxPosAngleYAction",
2366                                     _("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2367                                     "tools.shapes.3dbox", "dir_vp_y", persp->get_vanishing_point (Box3D::Y)->get_angle(),
2368                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2369                                     0.0, 360.0, 1.0, 10.0,
2370                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2371                                     sp_3dbox_vpy_angle_changed,
2372                                     0.1, 1);
2373     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2374     g_object_set_data(holder, "box3d_angle_y_action", eact);
2375     if (!persp->get_vanishing_point (Box3D::Y)->is_finite()) {
2376         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2377     } else {
2378         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2379     }
2381     /* toggle VP in Y direction */
2382     {
2383     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2384                                                  _("Toggle VP in Y direction"),
2385                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2386                                                  "toggle_vp_y",
2387                                                  Inkscape::ICON_SIZE_DECORATION);
2388     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2389     if (persp) {
2390         toggled = !persp->get_vanishing_point(Box3D::Y)->is_finite();
2391     }
2392     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2393     /* we connect the signal after setting the state to avoid switching the state again */
2394     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_y_changed), holder);
2395     }
2397     /* angle of VP in Z direction */
2398     eact = create_adjustment_action("3DBoxPosAngleZAction",
2399                                     _("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2400                                     "tools.shapes.3dbox", "dir_vp_z", persp->get_vanishing_point (Box3D::Z)->get_angle(),
2401                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2402                                      0.0, 360.0, 1.0, 10.0,
2403                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2404                                     sp_3dbox_vpz_angle_changed,
2405                                     0.1, 1);
2407     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2408     g_object_set_data(holder, "box3d_angle_z_action", eact);
2409     if (!persp->get_vanishing_point (Box3D::Z)->is_finite()) {
2410         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2411     } else {
2412         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2413     }
2415     /* toggle VP in Z direction */
2416     {
2417     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2418                                                  _("Toggle VP in Z direction"),
2419                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2420                                                  "toggle_vp_z",
2421                                                  Inkscape::ICON_SIZE_DECORATION);
2422     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2423     if (persp) {
2424         toggled = !persp->get_vanishing_point(Box3D::Z)->is_finite();
2425     }
2426     /* we connect the signal after setting the state to avoid switching the state again */
2427     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2428     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_z_changed), holder);
2429     }
2431     sigc::connection *connection = new sigc::connection(
2432         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_3dbox_toolbox_selection_changed), (GObject *)holder))
2433        );
2434     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2435     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2438 //########################
2439 //##       Spiral       ##
2440 //########################
2442 static void
2443 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2445     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2447     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2448         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2449     }
2451     // quit if run by the attr_changed listener
2452     if (g_object_get_data( tbl, "freeze" )) {
2453         return;
2454     }
2456     // in turn, prevent listener from responding
2457     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2459     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2461     bool modmade = false;
2462     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2463          items != NULL;
2464          items = items->next)
2465     {
2466         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2467             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2468             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2469             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2470             modmade = true;
2471         }
2472     }
2474     g_free(namespaced_name);
2476     if (modmade) {
2477         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2478                                    _("Change spiral"));
2479     }
2481     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2484 static void
2485 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2487     sp_spl_tb_value_changed(adj, tbl, "revolution");
2490 static void
2491 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2493     sp_spl_tb_value_changed(adj, tbl, "expansion");
2496 static void
2497 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2499     sp_spl_tb_value_changed(adj, tbl, "t0");
2502 static void
2503 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2505     GtkWidget *tbl = GTK_WIDGET(obj);
2507     GtkAdjustment *adj;
2509     // fixme: make settable
2510     gdouble rev = 5;
2511     gdouble exp = 1.0;
2512     gdouble t0 = 0.0;
2514     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2515     gtk_adjustment_set_value(adj, rev);
2516     gtk_adjustment_value_changed(adj);
2518     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2519     gtk_adjustment_set_value(adj, exp);
2520     gtk_adjustment_value_changed(adj);
2522     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2523     gtk_adjustment_set_value(adj, t0);
2524     gtk_adjustment_value_changed(adj);
2526     spinbutton_defocus(GTK_OBJECT(tbl));
2530 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2531                                          gchar const */*old_value*/, gchar const */*new_value*/,
2532                                          bool /*is_interactive*/, gpointer data)
2534     GtkWidget *tbl = GTK_WIDGET(data);
2536     // quit if run by the _changed callbacks
2537     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2538         return;
2539     }
2541     // in turn, prevent callbacks from responding
2542     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2544     GtkAdjustment *adj;
2545     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2546     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2548     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2549     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2551     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2552     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2554     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2558 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2559     NULL, /* child_added */
2560     NULL, /* child_removed */
2561     spiral_tb_event_attr_changed,
2562     NULL, /* content_changed */
2563     NULL  /* order_changed */
2564 };
2566 static void
2567 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2569     int n_selected = 0;
2570     Inkscape::XML::Node *repr = NULL;
2572     purge_repr_listener( tbl, tbl );
2574     for (GSList const *items = selection->itemList();
2575          items != NULL;
2576          items = items->next)
2577     {
2578         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2579             n_selected++;
2580             repr = SP_OBJECT_REPR((SPItem *) items->data);
2581         }
2582     }
2584     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2586     if (n_selected == 0) {
2587         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2588     } else if (n_selected == 1) {
2589         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2591         if (repr) {
2592             g_object_set_data( tbl, "repr", repr );
2593             Inkscape::GC::anchor(repr);
2594             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2595             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2596         }
2597     } else {
2598         // FIXME: implement averaging of all parameters for multiple selected
2599         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2600         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2601     }
2605 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2607     EgeAdjustmentAction* eact = 0;
2609     {
2610         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2611         ege_output_action_set_use_markup( act, TRUE );
2612         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2613         g_object_set_data( holder, "mode_action", act );
2614     }
2616     /* Revolution */
2617     {
2618         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2619         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2620         eact = create_adjustment_action( "SpiralRevolutionAction",
2621                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2622                                          "tools.shapes.spiral", "revolution", 3.0,
2623                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2624                                          0.01, 1024.0, 0.1, 1.0,
2625                                          labels, values, G_N_ELEMENTS(labels),
2626                                          sp_spl_tb_revolution_value_changed, 1, 2);
2627         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2628     }
2630     /* Expansion */
2631     {
2632         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2633         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2634         eact = create_adjustment_action( "SpiralExpansionAction",
2635                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2636                                          "tools.shapes.spiral", "expansion", 1.0,
2637                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2638                                          0.0, 1000.0, 0.01, 1.0,
2639                                          labels, values, G_N_ELEMENTS(labels),
2640                                          sp_spl_tb_expansion_value_changed);
2641         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2642     }
2644     /* T0 */
2645     {
2646         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2647         gdouble values[] = {0, 0.5, 0.9};
2648         eact = create_adjustment_action( "SpiralT0Action",
2649                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2650                                          "tools.shapes.spiral", "t0", 0.0,
2651                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2652                                          0.0, 0.999, 0.01, 1.0,
2653                                          labels, values, G_N_ELEMENTS(labels),
2654                                          sp_spl_tb_t0_value_changed);
2655         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2656     }
2658     /* Reset */
2659     {
2660         InkAction* inky = ink_action_new( "SpiralResetAction",
2661                                           _("Defaults"),
2662                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2663                                           GTK_STOCK_CLEAR,
2664                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2665         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2666         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2667     }
2670     sigc::connection *connection = new sigc::connection(
2671         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2672         );
2673     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2674     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2677 //########################
2678 //##     Pen/Pencil    ##
2679 //########################
2682 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2684     // Put stuff here
2687 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2689     // Put stuff here
2692 //########################
2693 //##       Tweak        ##
2694 //########################
2696 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2698     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2701 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2703     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2706 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2708     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2711 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2713     int mode = ege_select_one_action_get_active( act );
2714     prefs_set_int_attribute("tools.tweak", "mode", mode);
2716     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2717     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2718     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2719     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2720     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2721     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2722     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2723         if (doh) gtk_action_set_sensitive (doh, TRUE);
2724         if (dos) gtk_action_set_sensitive (dos, TRUE);
2725         if (dol) gtk_action_set_sensitive (dol, TRUE);
2726         if (doo) gtk_action_set_sensitive (doo, TRUE);
2727         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2728         if (fid) gtk_action_set_sensitive (fid, FALSE);
2729     } else {
2730         if (doh) gtk_action_set_sensitive (doh, FALSE);
2731         if (dos) gtk_action_set_sensitive (dos, FALSE);
2732         if (dol) gtk_action_set_sensitive (dol, FALSE);
2733         if (doo) gtk_action_set_sensitive (doo, FALSE);
2734         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2735         if (fid) gtk_action_set_sensitive (fid, TRUE);
2736     }
2739 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2741     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2744 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2745     bool show = gtk_toggle_action_get_active( act );
2746     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2748 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2749     bool show = gtk_toggle_action_get_active( act );
2750     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2752 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
2753     bool show = gtk_toggle_action_get_active( act );
2754     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2756 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
2757     bool show = gtk_toggle_action_get_active( act );
2758     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
2761 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2763     {
2764         /* Width */
2765         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2766         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2767         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2768                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2769                                                               "tools.tweak", "width", 15,
2770                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2771                                                               1, 100, 1.0, 10.0,
2772                                                               labels, values, G_N_ELEMENTS(labels),
2773                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2774         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2775         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2776     }
2779     {
2780         /* Force */
2781         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2782         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2783         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2784                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
2785                                                               "tools.tweak", "force", 20,
2786                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2787                                                               1, 100, 1.0, 10.0,
2788                                                               labels, values, G_N_ELEMENTS(labels),
2789                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2790         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2791         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2792     }
2794     /* Mode */
2795     {
2796         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2798         GtkTreeIter iter;
2799         gtk_list_store_append( model, &iter );
2800         gtk_list_store_set( model, &iter,
2801                             0, _("Push mode"),
2802                             1, _("Push parts of paths in any direction"),
2803                             2, "tweak_push_mode",
2804                             -1 );
2806         gtk_list_store_append( model, &iter );
2807         gtk_list_store_set( model, &iter,
2808                             0, _("Shrink mode"),
2809                             1, _("Shrink (inset) parts of paths"),
2810                             2, "tweak_shrink_mode",
2811                             -1 );
2813         gtk_list_store_append( model, &iter );
2814         gtk_list_store_set( model, &iter,
2815                             0, _("Grow mode"),
2816                             1, _("Grow (outset) parts of paths"),
2817                             2, "tweak_grow_mode",
2818                             -1 );
2820         gtk_list_store_append( model, &iter );
2821         gtk_list_store_set( model, &iter,
2822                             0, _("Attract mode"),
2823                             1, _("Attract parts of paths towards cursor"),
2824                             2, "tweak_attract_mode",
2825                             -1 );
2827         gtk_list_store_append( model, &iter );
2828         gtk_list_store_set( model, &iter,
2829                             0, _("Repel mode"),
2830                             1, _("Repel parts of paths from cursor"),
2831                             2, "tweak_repel_mode",
2832                             -1 );
2834         gtk_list_store_append( model, &iter );
2835         gtk_list_store_set( model, &iter,
2836                             0, _("Roughen mode"),
2837                             1, _("Roughen parts of paths"),
2838                             2, "tweak_roughen_mode",
2839                             -1 );
2841         gtk_list_store_append( model, &iter );
2842         gtk_list_store_set( model, &iter,
2843                             0, _("Color paint mode"),
2844                             1, _("Paint the tool's color upon selected objects"),
2845                             2, "tweak_colorpaint_mode",
2846                             -1 );
2848         gtk_list_store_append( model, &iter );
2849         gtk_list_store_set( model, &iter,
2850                             0, _("Color jitter mode"),
2851                             1, _("Jitter the colors of selected objects"),
2852                             2, "tweak_colorjitter_mode",
2853                             -1 );
2855         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
2856         g_object_set( act, "short_label", _("Mode:"), NULL );
2857         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2858         g_object_set_data( holder, "mode_action", act );
2860         ege_select_one_action_set_appearance( act, "full" );
2861         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2862         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2863         ege_select_one_action_set_icon_column( act, 2 );
2864         ege_select_one_action_set_tooltip_column( act, 1  );
2866         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2867         ege_select_one_action_set_active( act, mode );
2868         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
2870         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
2871     }
2873     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2875     {
2876         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
2877         ege_output_action_set_use_markup( act, TRUE );
2878         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2879         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
2880             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2881         g_object_set_data( holder, "tweak_channels_label", act);
2882     }
2884     {
2885         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
2886                                                       _("Hue"),
2887                                                       _("In color mode, act on objects' hue"),
2888                                                       NULL,
2889                                                       Inkscape::ICON_SIZE_DECORATION );
2890         g_object_set( act, "short_label", _("H"), NULL );
2891         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2892         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
2893         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
2894         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
2895             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2896         g_object_set_data( holder, "tweak_doh", act);
2897     }
2898     {
2899         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
2900                                                       _("Saturation"),
2901                                                       _("In color mode, act on objects' saturation"),
2902                                                       NULL,
2903                                                       Inkscape::ICON_SIZE_DECORATION );
2904         g_object_set( act, "short_label", _("S"), NULL );
2905         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2906         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
2907         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
2908         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
2909             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2910         g_object_set_data( holder, "tweak_dos", act );
2911     }
2912     {
2913         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
2914                                                       _("Lightness"),
2915                                                       _("In color mode, act on objects' lightness"),
2916                                                       NULL,
2917                                                       Inkscape::ICON_SIZE_DECORATION );
2918         g_object_set( act, "short_label", _("L"), NULL );
2919         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2920         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
2921         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
2922         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
2923             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2924         g_object_set_data( holder, "tweak_dol", act );
2925     }
2926     {
2927         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
2928                                                       _("Opacity"),
2929                                                       _("In color mode, act on objects' opacity"),
2930                                                       NULL,
2931                                                       Inkscape::ICON_SIZE_DECORATION );
2932         g_object_set( act, "short_label", _("O"), NULL );
2933         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2934         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
2935         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
2936         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
2937             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
2938         g_object_set_data( holder, "tweak_doo", act );
2939     }
2941     {   /* Fidelity */
2942         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
2943         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
2944         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
2945                                                               _("Fidelity"), _("Fidelity:"),
2946                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
2947                                                               "tools.tweak", "fidelity", 50,
2948                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
2949                                                               1, 100, 1.0, 10.0,
2950                                                               labels, values, G_N_ELEMENTS(labels),
2951                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
2952         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2953         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2954         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
2955             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
2956         g_object_set_data( holder, "tweak_fidelity", eact );
2957     }
2960     /* Use Pressure button */
2961     {
2962         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
2963                                                       _("Pressure"),
2964                                                       _("Use the pressure of the input device to alter the force of tweak action"),
2965                                                       "use_pressure",
2966                                                       Inkscape::ICON_SIZE_DECORATION );
2967         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2968         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
2969         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
2970     }
2975 //########################
2976 //##     Calligraphy    ##
2977 //########################
2979 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
2981     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2984 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
2986     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2989 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
2991     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2994 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2996     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2999 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3001     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3004 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3006     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3009 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3011     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3014 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3016     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3019 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3021     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3024 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3026     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3029 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3031     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3033     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3036 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3038     // FIXME: make defaults settable via Inkscape Options
3039     struct KeyValue {
3040         char const *key;
3041         double value;
3042     } const key_values[] = {
3043         {"mass", 0.02},
3044         {"wiggle", 0.0},
3045         {"angle", 30.0},
3046         {"width", 15},
3047         {"thinning", 0.1},
3048         {"tremor", 0.0},
3049         {"flatness", 0.9},
3050         {"cap_rounding", 0.0}
3051     };
3053     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3054         KeyValue const &kv = key_values[i];
3055         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3056         if ( adj ) {
3057             gtk_adjustment_set_value(adj, kv.value);
3058         }
3059     }
3063 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3065     {
3066         EgeAdjustmentAction* calligraphy_angle = 0;
3068         {
3069         /* Width */
3070         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3071         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3072         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3073                                                               _("Pen Width"), _("Width:"),
3074                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3075                                                               "tools.calligraphic", "width", 15,
3076                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3077                                                               1, 100, 1.0, 10.0,
3078                                                               labels, values, G_N_ELEMENTS(labels),
3079                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3080         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3081         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3082         }
3084         {
3085         /* Thinning */
3086             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3087             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3088         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3089                                                               _("Stroke Thinning"), _("Thinning:"),
3090                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3091                                                               "tools.calligraphic", "thinning", 0.1,
3092                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3093                                                               -1.0, 1.0, 0.01, 0.1,
3094                                                               labels, values, G_N_ELEMENTS(labels),
3095                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3096         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3097         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3098         }
3100         {
3101         /* Angle */
3102         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3103         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3104         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3105                                                               _("Pen Angle"), _("Angle:"),
3106                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3107                                                               "tools.calligraphic", "angle", 30,
3108                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3109                                                               -90.0, 90.0, 1.0, 10.0,
3110                                                               labels, values, G_N_ELEMENTS(labels),
3111                                                               sp_ddc_angle_value_changed, 1, 0 );
3112         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3113         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3114         calligraphy_angle = eact;
3115         }
3117         {
3118         /* Fixation */
3119             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3120         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3121         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3122                                                               _("Fixation"), _("Fixation:"),
3123                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3124                                                               "tools.calligraphic", "flatness", 0.9,
3125                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3126                                                               0.0, 1.0, 0.01, 0.1,
3127                                                               labels, values, G_N_ELEMENTS(labels),
3128                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3129         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3130         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3131         }
3133         {
3134         /* Cap Rounding */
3135             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3136         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3137         // TRANSLATORS: "cap" means "end" (both start and finish) here
3138         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3139                                                               _("Cap rounding"), _("Caps:"),
3140                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3141                                                               "tools.calligraphic", "cap_rounding", 0.0,
3142                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3143                                                               0.0, 5.0, 0.01, 0.1,
3144                                                               labels, values, G_N_ELEMENTS(labels),
3145                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3146         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3147         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3148         }
3150         {
3151         /* Tremor */
3152             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3153         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3154         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3155                                                               _("Stroke Tremor"), _("Tremor:"),
3156                                                               _("Increase to make strokes rugged and trembling"),
3157                                                               "tools.calligraphic", "tremor", 0.0,
3158                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3159                                                               0.0, 1.0, 0.01, 0.1,
3160                                                               labels, values, G_N_ELEMENTS(labels),
3161                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3163         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3164         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3165         }
3167         {
3168         /* Wiggle */
3169         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3170         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3171         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3172                                                               _("Pen Wiggle"), _("Wiggle:"),
3173                                                               _("Increase to make the pen waver and wiggle"),
3174                                                               "tools.calligraphic", "wiggle", 0.0,
3175                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3176                                                               0.0, 1.0, 0.01, 0.1,
3177                                                               labels, values, G_N_ELEMENTS(labels),
3178                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3179         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3180         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3181         }
3183         {
3184         /* Mass */
3185             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3186         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3187         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3188                                                               _("Pen Mass"), _("Mass:"),
3189                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3190                                                               "tools.calligraphic", "mass", 0.02,
3191                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3192                                                               0.0, 1.0, 0.01, 0.1,
3193                                                               labels, values, G_N_ELEMENTS(labels),
3194                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3195         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3196         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3197         }
3200         /* Trace Background button */
3201         {
3202             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3203                                                           _("Trace Background"),
3204                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3205                                                           "trace_background",
3206                                                           Inkscape::ICON_SIZE_DECORATION );
3207             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3208             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3209             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3210         }
3212         /* Use Pressure button */
3213         {
3214             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3215                                                           _("Pressure"),
3216                                                           _("Use the pressure of the input device to alter the width of the pen"),
3217                                                           "use_pressure",
3218                                                           Inkscape::ICON_SIZE_DECORATION );
3219             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3220             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3221             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3222         }
3224         /* Use Tilt button */
3225         {
3226             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3227                                                           _("Tilt"),
3228                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3229                                                           "use_tilt",
3230                                                           Inkscape::ICON_SIZE_DECORATION );
3231             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3232             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3233             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3234             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3235         }
3237         /* Reset */
3238         {
3239             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3240                                              _("Defaults"),
3241                                              _("Reset all parameters to defaults"),
3242                                              GTK_STOCK_CLEAR );
3243             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3244             gtk_action_group_add_action( mainActions, act );
3245             gtk_action_set_sensitive( act, TRUE );
3246         }
3247     }
3251 //########################
3252 //##    Circle / Arc    ##
3253 //########################
3255 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3257     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3258     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3260     if (v1 == 0 && v2 == 0) {
3261         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3262             gtk_action_set_sensitive( ocb, FALSE );
3263             gtk_action_set_sensitive( make_whole, FALSE );
3264         }
3265     } else {
3266         gtk_action_set_sensitive( ocb, TRUE );
3267         gtk_action_set_sensitive( make_whole, TRUE );
3268     }
3271 static void
3272 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3274     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3276     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3277         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3278     }
3280     // quit if run by the attr_changed listener
3281     if (g_object_get_data( tbl, "freeze" )) {
3282         return;
3283     }
3285     // in turn, prevent listener from responding
3286     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3288     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3290     bool modmade = false;
3291     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3292          items != NULL;
3293          items = items->next)
3294     {
3295         SPItem *item = SP_ITEM(items->data);
3297         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3299             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3300             SPArc *arc = SP_ARC(item);
3302             if (!strcmp(value_name, "start"))
3303                 ge->start = (adj->value * M_PI)/ 180;
3304             else
3305                 ge->end = (adj->value * M_PI)/ 180;
3307             sp_genericellipse_normalize(ge);
3308             ((SPObject *)arc)->updateRepr();
3309             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3311             modmade = true;
3312         }
3313     }
3315     g_free(namespaced_name);
3317     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3319     sp_arctb_sensitivize( tbl, adj->value, other->value );
3321     if (modmade) {
3322         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3323                                    _("Arc: Change start/end"));
3324     }
3326     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3330 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3332     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3335 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3337     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3340 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3342     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3343     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3344         if ( ege_select_one_action_get_active( act ) != 0 ) {
3345             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3346         } else {
3347             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3348         }
3349     }
3351     // quit if run by the attr_changed listener
3352     if (g_object_get_data( tbl, "freeze" )) {
3353         return;
3354     }
3356     // in turn, prevent listener from responding
3357     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3359     bool modmade = false;
3361     if ( ege_select_one_action_get_active(act) != 0 ) {
3362         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3363              items != NULL;
3364              items = items->next)
3365         {
3366             if (SP_IS_ARC((SPItem *) items->data)) {
3367                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3368                 repr->setAttribute("sodipodi:open", "true");
3369                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3370                 modmade = true;
3371             }
3372         }
3373     } else {
3374         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3375              items != NULL;
3376              items = items->next)
3377         {
3378             if (SP_IS_ARC((SPItem *) items->data))    {
3379                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3380                 repr->setAttribute("sodipodi:open", NULL);
3381                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3382                 modmade = true;
3383             }
3384         }
3385     }
3387     if (modmade) {
3388         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3389                                    _("Arc: Change open/closed"));
3390     }
3392     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3395 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3397     GtkAdjustment *adj;
3398     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3399     gtk_adjustment_set_value(adj, 0.0);
3400     gtk_adjustment_value_changed(adj);
3402     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3403     gtk_adjustment_set_value(adj, 0.0);
3404     gtk_adjustment_value_changed(adj);
3406     spinbutton_defocus( GTK_OBJECT(obj) );
3409 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3410                                       gchar const */*old_value*/, gchar const */*new_value*/,
3411                                       bool /*is_interactive*/, gpointer data)
3413     GObject *tbl = G_OBJECT(data);
3415     // quit if run by the _changed callbacks
3416     if (g_object_get_data( tbl, "freeze" )) {
3417         return;
3418     }
3420     // in turn, prevent callbacks from responding
3421     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3423     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3424     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3426     GtkAdjustment *adj1,*adj2;
3427     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3428     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3429     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3430     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3432     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3434     char const *openstr = NULL;
3435     openstr = repr->attribute("sodipodi:open");
3436     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3438     if (openstr) {
3439         ege_select_one_action_set_active( ocb, 1 );
3440     } else {
3441         ege_select_one_action_set_active( ocb, 0 );
3442     }
3444     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3447 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3448     NULL, /* child_added */
3449     NULL, /* child_removed */
3450     arc_tb_event_attr_changed,
3451     NULL, /* content_changed */
3452     NULL  /* order_changed */
3453 };
3456 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3458     int n_selected = 0;
3459     Inkscape::XML::Node *repr = NULL;
3461     purge_repr_listener( tbl, tbl );
3463     for (GSList const *items = selection->itemList();
3464          items != NULL;
3465          items = items->next)
3466     {
3467         if (SP_IS_ARC((SPItem *) items->data)) {
3468             n_selected++;
3469             repr = SP_OBJECT_REPR((SPItem *) items->data);
3470         }
3471     }
3473     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3475     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3476     if (n_selected == 0) {
3477         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3478     } else if (n_selected == 1) {
3479         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3480         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3482         if (repr) {
3483             g_object_set_data( tbl, "repr", repr );
3484             Inkscape::GC::anchor(repr);
3485             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3486             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3487         }
3488     } else {
3489         // FIXME: implement averaging of all parameters for multiple selected
3490         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3491         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3492         sp_arctb_sensitivize( tbl, 1, 0 );
3493     }
3497 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3499     EgeAdjustmentAction* eact = 0;
3502     {
3503         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3504         ege_output_action_set_use_markup( act, TRUE );
3505         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3506         g_object_set_data( holder, "mode_action", act );
3507     }
3509     /* Start */
3510     {
3511         eact = create_adjustment_action( "ArcStartAction",
3512                                          _("Start"), _("Start:"),
3513                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3514                                          "tools.shapes.arc", "start", 0.0,
3515                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3516                                          -360.0, 360.0, 1.0, 10.0,
3517                                          0, 0, 0,
3518                                          sp_arctb_start_value_changed);
3519         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3520     }
3522     /* End */
3523     {
3524         eact = create_adjustment_action( "ArcEndAction",
3525                                          _("End"), _("End:"),
3526                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3527                                          "tools.shapes.arc", "end", 0.0,
3528                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3529                                          -360.0, 360.0, 1.0, 10.0,
3530                                          0, 0, 0,
3531                                          sp_arctb_end_value_changed);
3532         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3533     }
3535     /* Segments / Pie checkbox */
3536     {
3537         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3539         GtkTreeIter iter;
3540         gtk_list_store_append( model, &iter );
3541         gtk_list_store_set( model, &iter,
3542                             0, _("Closed arc"),
3543                             1, _("Switch to segment (closed shape with two radii)"),
3544                             2, "circle_closed_arc",
3545                             -1 );
3547         gtk_list_store_append( model, &iter );
3548         gtk_list_store_set( model, &iter,
3549                             0, _("Open Arc"),
3550                             1, _("Switch to arc (unclosed shape)"),
3551                             2, "circle_open_arc",
3552                             -1 );
3554         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3555         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3556         g_object_set_data( holder, "open_action", act );
3558         ege_select_one_action_set_appearance( act, "full" );
3559         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3560         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3561         ege_select_one_action_set_icon_column( act, 2 );
3562         ege_select_one_action_set_tooltip_column( act, 1  );
3564         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3565         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3566         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3567         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3568     }
3570     /* Make Whole */
3571     {
3572         InkAction* inky = ink_action_new( "ArcResetAction",
3573                                           _("Make whole"),
3574                                           _("Make the shape a whole ellipse, not arc or segment"),
3575                                           "reset_circle",
3576                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3577         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3578         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3579         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3580         g_object_set_data( holder, "make_whole", inky );
3581     }
3583     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3584     // sensitivize make whole and open checkbox
3585     {
3586         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3587         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3588         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3589     }
3592     sigc::connection *connection = new sigc::connection(
3593         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3594         );
3595     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3596     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3602 // toggle button callbacks and updaters
3604 //########################
3605 //##      Dropper       ##
3606 //########################
3608 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3609     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3610     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3611     if ( set_action ) {
3612         if ( gtk_toggle_action_get_active( act ) ) {
3613             gtk_action_set_sensitive( set_action, TRUE );
3614         } else {
3615             gtk_action_set_sensitive( set_action, FALSE );
3616         }
3617     }
3619     spinbutton_defocus(GTK_OBJECT(tbl));
3622 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3623     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3624     spinbutton_defocus(GTK_OBJECT(tbl));
3628 /**
3629  * Dropper auxiliary toolbar construction and setup.
3630  *
3631  * TODO: Would like to add swatch of current color.
3632  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3633  *       can drag and drop places. Will provide a nice mixing palette.
3634  */
3635 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3637     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3639     {
3640         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3641                                                       _("Pick alpha"),
3642                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3643                                                       "color_alpha_get",
3644                                                       Inkscape::ICON_SIZE_DECORATION );
3645         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3646         g_object_set_data( holder, "pick_action", act );
3647         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3648         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3649     }
3651     {
3652         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3653                                                       _("Set alpha"),
3654                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3655                                                       "color_alpha_set",
3656                                                       Inkscape::ICON_SIZE_DECORATION );
3657         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3658         g_object_set_data( holder, "set_action", act );
3659         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3660         // make sure it's disabled if we're not picking alpha
3661         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3662         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3663     }
3667 //########################
3668 //##    Text Toolbox    ##
3669 //########################
3670 /*
3671 static void
3672 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3674     //Call back for letter sizing spinbutton
3677 static void
3678 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3680     //Call back for line height spinbutton
3683 static void
3684 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3686     //Call back for horizontal kerning spinbutton
3689 static void
3690 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3692     //Call back for vertical kerning spinbutton
3695 static void
3696 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3698     //Call back for letter rotation spinbutton
3699 }*/
3701 namespace {
3703 bool visible = false;
3705 void
3706 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3708     SPStyle *query =
3709         sp_style_new (SP_ACTIVE_DOCUMENT);
3711     int result_family =
3712         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3714     int result_style =
3715         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3717     int result_numbers =
3718         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3720     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3722     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3723     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3724     {
3725         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3727         if (repr)
3728         {
3729             sp_style_read_from_repr (query, repr);
3730         }
3731         else
3732         {
3733             return;
3734         }
3735     }
3737     if (query->text)
3738     {
3739         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3740             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3741             gtk_entry_set_text (GTK_ENTRY (entry), "");
3743         } else if (query->text->font_family.value) {
3745             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3746             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3748             Gtk::TreePath path;
3749             try {
3750                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3751             } catch (...) {
3752                 return;
3753             }
3755             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3756             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3758             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3760             gtk_tree_selection_select_path (tselection, path.gobj());
3761             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3763             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3764         }
3766         //Size
3767         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3768         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3769         g_object_set_data (tbl, "size-block", gpointer(1));
3770         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3771         g_object_set_data (tbl, "size-block", gpointer(0));
3772         free (str);
3774         //Anchor
3775         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3776         {
3777             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3778             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3779             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3780             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3781         }
3782         else
3783         {
3784             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3785             {
3786                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3787                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3788                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3789                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3790             }
3791             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3792             {
3793                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3794                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3795                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3796                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3797             }
3798             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3799             {
3800                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3801                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3802                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3803                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3804             }
3805         }
3807         //Style
3808         {
3809             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3811             gboolean active = gtk_toggle_button_get_active (button);
3812             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3814             if (active != check)
3815             {
3816                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3817                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3818                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3819             }
3820         }
3822         {
3823             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3825             gboolean active = gtk_toggle_button_get_active (button);
3826             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3828             if (active != check)
3829             {
3830                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3831                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3832                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3833             }
3834         }
3836         //Orientation
3837         //locking both buttons, changing one affect all group (both)
3838         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3839         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3841         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3842         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3844         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3845         {
3846             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3847         }
3848         else
3849         {
3850             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3851         }
3852         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3853         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3854     }
3856     sp_style_unref(query);
3859 void
3860 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
3862     sp_text_toolbox_selection_changed (selection, tbl);
3865 void
3866 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
3868     sp_text_toolbox_selection_changed (NULL, tbl);
3871 void
3872 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3873                                 GObject             *tbl)
3875     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3876     GtkTreeModel *model = 0;
3877     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3878     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3879     GtkTreeIter   iter;
3880     char         *family = 0;
3882     (void)popdown;
3884     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3885     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3887     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3888         return;
3889     }
3891     gtk_tree_model_get (model, &iter, 0, &family, -1);
3893     if (g_object_get_data (G_OBJECT (selection), "block"))
3894     {
3895         gtk_entry_set_text (GTK_ENTRY (entry), family);
3896         return;
3897     }
3899     gtk_entry_set_text (GTK_ENTRY (entry), family);
3901     SPStyle *query =
3902         sp_style_new (SP_ACTIVE_DOCUMENT);
3904     int result_numbers =
3905         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3907     SPCSSAttr *css = sp_repr_css_attr_new ();
3908     sp_repr_css_set_property (css, "font-family", family);
3910     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3911     if (result_numbers == QUERY_STYLE_NOTHING)
3912     {
3913         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3914         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3915     }
3916     else
3917     {
3918         sp_desktop_set_style (desktop, css, true, true);
3919     }
3921     sp_style_unref(query);
3923     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3924                                    _("Text: Change font family"));
3925     sp_repr_css_attr_unref (css);
3926     free (family);
3927     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3929     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3932 void
3933 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3934                                        GObject      *tbl)
3936     const char *family = gtk_entry_get_text (entry);
3938     try {
3939         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3940         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3941         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3942         gtk_tree_selection_select_path (selection, path.gobj());
3943         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3944         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3945     } catch (...) {
3946         if (family && strlen (family))
3947         {
3948             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3949         }
3950     }
3953 void
3954 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3955                                    gpointer          data)
3957     if (g_object_get_data (G_OBJECT (button), "block")) return;
3958     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3959     int prop = GPOINTER_TO_INT(data);
3961     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3962     SPCSSAttr *css = sp_repr_css_attr_new ();
3964     switch (prop)
3965     {
3966         case 0:
3967         {
3968             sp_repr_css_set_property (css, "text-anchor", "start");
3969             sp_repr_css_set_property (css, "text-align", "start");
3970             break;
3971         }
3972         case 1:
3973         {
3974             sp_repr_css_set_property (css, "text-anchor", "middle");
3975             sp_repr_css_set_property (css, "text-align", "center");
3976             break;
3977         }
3979         case 2:
3980         {
3981             sp_repr_css_set_property (css, "text-anchor", "end");
3982             sp_repr_css_set_property (css, "text-align", "end");
3983             break;
3984         }
3986         case 3:
3987         {
3988             sp_repr_css_set_property (css, "text-anchor", "start");
3989             sp_repr_css_set_property (css, "text-align", "justify");
3990             break;
3991         }
3992     }
3994     SPStyle *query =
3995         sp_style_new (SP_ACTIVE_DOCUMENT);
3996     int result_numbers =
3997         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3999     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4000     if (result_numbers == QUERY_STYLE_NOTHING)
4001     {
4002         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4003     }
4005     sp_style_unref(query);
4007     sp_desktop_set_style (desktop, css, true, true);
4008     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4009                                    _("Text: Change alignment"));
4010     sp_repr_css_attr_unref (css);
4012     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4015 void
4016 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4017                                gpointer          data)
4019     if (g_object_get_data (G_OBJECT (button), "block")) return;
4021     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4022     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4023     int          prop       = GPOINTER_TO_INT(data);
4024     bool         active     = gtk_toggle_button_get_active (button);
4027     switch (prop)
4028     {
4029         case 0:
4030         {
4031             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4032             break;
4033         }
4035         case 1:
4036         {
4037             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4038             break;
4039         }
4040     }
4042     SPStyle *query =
4043         sp_style_new (SP_ACTIVE_DOCUMENT);
4044     int result_numbers =
4045         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4047     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4048     if (result_numbers == QUERY_STYLE_NOTHING)
4049     {
4050         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4051     }
4053     sp_style_unref(query);
4055     sp_desktop_set_style (desktop, css, true, true);
4056     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4057                                    _("Text: Change font style"));
4058     sp_repr_css_attr_unref (css);
4060     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4063 void
4064 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4065                                      gpointer         data)
4067     if (g_object_get_data (G_OBJECT (button), "block")) {
4068         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4069         return;
4070     }
4072     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4073     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4074     int          prop       = GPOINTER_TO_INT(data);
4076     switch (prop)
4077     {
4078         case 0:
4079         {
4080             sp_repr_css_set_property (css, "writing-mode", "lr");
4081             break;
4082         }
4084         case 1:
4085         {
4086             sp_repr_css_set_property (css, "writing-mode", "tb");
4087             break;
4088         }
4089     }
4091     SPStyle *query =
4092         sp_style_new (SP_ACTIVE_DOCUMENT);
4093     int result_numbers =
4094         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4096     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4097     if (result_numbers == QUERY_STYLE_NOTHING)
4098     {
4099         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4100     }
4102     sp_desktop_set_style (desktop, css, true, true);
4103     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4104                                    _("Text: Change orientation"));
4105     sp_repr_css_attr_unref (css);
4107     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4110 gboolean
4111 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4113     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4114     if (!desktop) return FALSE;
4116     switch (get_group0_keyval (event)) {
4117         case GDK_Escape: // defocus
4118             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4119             return TRUE; // I consumed the event
4120             break;
4121         case GDK_Return: // defocus
4122         case GDK_KP_Enter:
4123             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4124             return TRUE; // I consumed the event
4125             break;
4126     }
4127     return FALSE;
4130 gboolean
4131 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4133     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4134     if (!desktop) return FALSE;
4136     switch (get_group0_keyval (event)) {
4137         case GDK_Escape: // defocus
4138             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4139             sp_text_toolbox_selection_changed (NULL, tbl); // update
4140             return TRUE; // I consumed the event
4141             break;
4142     }
4143     return FALSE;
4146 gboolean
4147 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4149     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4150     if (!desktop) return FALSE;
4152     switch (get_group0_keyval (event)) {
4153         case GDK_KP_Enter:
4154         case GDK_Return:
4155         case GDK_Escape: // defocus
4156             gtk_widget_hide (w);
4157             visible = false;
4158             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4159             return TRUE; // I consumed the event
4160             break;
4161     }
4162     return FALSE;
4166 void
4167 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4168                                GObject     *tbl)
4170     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4172     if (g_object_get_data (tbl, "size-block")) return;
4174 #if GTK_CHECK_VERSION(2,6,0)
4175     char *text = gtk_combo_box_get_active_text (cbox);
4176 #else // GTK_CHECK_VERSION(2,6,0)
4177     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
4178     GtkTreeIter iter;
4179     char *text = NULL;
4181     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
4182         gtk_tree_model_get (model, &iter, 0, &text, -1);
4183 #endif // GTK_CHECK_VERSION(2,6,0)
4185     SPCSSAttr *css = sp_repr_css_attr_new ();
4186     sp_repr_css_set_property (css, "font-size", text);
4187     free (text);
4189     SPStyle *query =
4190         sp_style_new (SP_ACTIVE_DOCUMENT);
4191     int result_numbers =
4192         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4194     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4195     if (result_numbers == QUERY_STYLE_NOTHING)
4196     {
4197         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4198     }
4200     sp_style_unref(query);
4202     sp_desktop_set_style (desktop, css, true, true);
4203     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4204                                    _("Text: Change font size"));
4205     sp_repr_css_attr_unref (css);
4208     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4209         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4212 void
4213 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4214                                          GObject            *tbl)
4216     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4217     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4218     int x, y;
4220     if (!visible)
4221     {
4222         gdk_window_get_origin (widget->window, &x, &y);
4223         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4224         gtk_widget_show_all (popdown);
4226         gdk_pointer_grab (widget->window, TRUE,
4227                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4228                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4229                                         GDK_POINTER_MOTION_MASK),
4230                           NULL, NULL, GDK_CURRENT_TIME);
4232         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4234         visible = true;
4235     }
4236     else
4237     {
4238         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4239         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4240         gtk_widget_hide (popdown);
4241         visible = false;
4242     }
4245 gboolean
4246 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4247                                  GdkEventFocus    */*event*/,
4248                                  GObject          */*tbl*/)
4250     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4251     return FALSE;
4254 gboolean
4255 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4256                                    GdkEventFocus    */*event*/,
4257                                    GObject          */*tbl*/)
4259     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4261     gtk_widget_hide (popdown);
4262     visible = false;
4263     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4264     return TRUE;
4267 void
4268 cell_data_func  (GtkTreeViewColumn */*column*/,
4269                  GtkCellRenderer   *cell,
4270                  GtkTreeModel      *tree_model,
4271                  GtkTreeIter       *iter,
4272                  gpointer           /*data*/)
4274     char        *family,
4275         *family_escaped,
4276         *sample_escaped;
4278     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4280     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4282     family_escaped = g_markup_escape_text (family, -1);
4283     sample_escaped = g_markup_escape_text (sample, -1);
4285     std::stringstream markup;
4286     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4287     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4289     free (family);
4290     free (family_escaped);
4291     free (sample_escaped);
4294 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4295     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4296     if (completion) {
4297         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4298         g_object_unref (completion);
4299     }
4302 GtkWidget*
4303 sp_text_toolbox_new (SPDesktop *desktop)
4305     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4307     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4308     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4310     GtkTooltips *tt = gtk_tooltips_new();
4311     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4313     ////////////Family
4314     //Window
4315     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4316     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4318     //Entry
4319     GtkWidget           *entry = gtk_entry_new ();
4320     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4321     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4322     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4323     gtk_entry_completion_set_text_column (completion, 0);
4324     gtk_entry_completion_set_minimum_key_length (completion, 1);
4325     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4326     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4327     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4328     aux_toolbox_space (tbl, 1);
4329     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4330     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4332     //Button
4333     GtkWidget   *button = gtk_button_new ();
4334     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4335     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4337     //Popdown
4338     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4339     GtkWidget           *treeview = gtk_tree_view_new ();
4341     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4342     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4343     gtk_tree_view_column_pack_start (column, cell, FALSE);
4344     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4345     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4346     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4348     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4349     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4350 #if GTK_CHECK_VERSION(2,6,0)
4351     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4352 #endif // GTK_CHECK_VERSION(2,6,0)
4354     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4356     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4357     gtk_container_add (GTK_CONTAINER (sw), treeview);
4359     gtk_container_add (GTK_CONTAINER (window), sw);
4360     gtk_widget_set_size_request (window, 300, 450);
4362     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4363     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4364     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4366     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4368     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4369     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4371     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4372     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4374     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4375     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4376     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4377     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4378     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4380     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4381     aux_toolbox_space (tbl, 1);
4382     GtkWidget *box = gtk_event_box_new ();
4383     gtk_container_add (GTK_CONTAINER (box), image);
4384     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4385     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4386     GtkTooltips *tooltips = gtk_tooltips_new ();
4387     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4388     gtk_widget_hide (GTK_WIDGET (box));
4389     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4391     ////////////Size
4392     const char *sizes[] = {
4393         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4394         "16", "18", "20", "22", "24", "28",
4395         "32", "36", "40", "48", "56", "64", "72", "144"
4396     };
4398     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4399     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4400     gtk_widget_set_size_request (cbox, 80, -1);
4401     aux_toolbox_space (tbl, 1);
4402     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4403     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4404     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4405     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4407     //spacer
4408     aux_toolbox_space (tbl, 4);
4409     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4411     ////////////Text anchor
4412     GtkWidget *group   = gtk_radio_button_new (NULL);
4413     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4414     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4416     // left
4417     GtkWidget *rbutton = group;
4418     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4419     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4420     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4422     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4423     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4424     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4425     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4427     // center
4428     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4429     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4430     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4431     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4433     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4434     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4435     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4436     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4438     // right
4439     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4440     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4441     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4442     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4444     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4445     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4446     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4447     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4449     // fill
4450     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4451     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4452     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4453     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4455     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4456     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4457     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4458     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4460     aux_toolbox_space (tbl, 1);
4461     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4463     //spacer
4464     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4466     ////////////Text style
4467     row = gtk_hbox_new (FALSE, 4);
4469     // bold
4470     rbutton = gtk_toggle_button_new ();
4471     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4472     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4473     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4474     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4476     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4477     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4478     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4480     // italic
4481     rbutton = gtk_toggle_button_new ();
4482     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4483     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4484     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4485     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4487     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4488     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4489     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4491     aux_toolbox_space (tbl, 1);
4492     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4494     //spacer
4495     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4497     ////////////Text orientation
4498     group   = gtk_radio_button_new (NULL);
4499     row     = gtk_hbox_new (FALSE, 4);
4500     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4502     // horizontal
4503     rbutton = group;
4504     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4505     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4506     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4507     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4509     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4510     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4511     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4513     // vertical
4514     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4515     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4516     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4517     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4518     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4520     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4521     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4522     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4523     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4526     //watch selection
4527     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4529     sigc::connection *c_selection_changed =
4530         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4531                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4532     pool->add_connection ("selection-changed", c_selection_changed);
4534     sigc::connection *c_selection_modified =
4535         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4536                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4537     pool->add_connection ("selection-modified", c_selection_modified);
4539     sigc::connection *c_subselection_changed =
4540         new sigc::connection (desktop->connectToolSubselectionChanged
4541                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4542     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4544     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4547 #if 0
4548     // horizontal
4549     {
4550         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4551         GtkWidget *b = group = gtk_radio_button_new (NULL);
4552         gtk_container_add (GTK_CONTAINER (b), px);
4553         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4554         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4555         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4556         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4557     }
4559     // vertical
4560     {
4561         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4562         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4563         gtk_container_add (GTK_CONTAINER (b), px);
4564         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4565         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4566         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4567         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4568     }
4570     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4572     // letter spacing
4573     {
4574         {
4575             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4576             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4577             gtk_container_add (GTK_CONTAINER (hb), image);
4578             gtk_widget_show(image);
4579             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4580         }
4582         {
4583             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between letters"),
4584                                              "tools.text", "letter_spacing", 0.0,
4585                                              us, tbl, FALSE, NULL,
4586                                              -1000.0, 1000.0, 0.1, 0.1,
4587                                              sp_text_letter_changed, 0.1, 1);
4588             gtk_widget_set_size_request (hb, 45, 6);
4589             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4590         }
4591     }
4593     // line spacing
4594     {
4595         {
4596             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4597             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4598             gtk_container_add (GTK_CONTAINER (hb), image);
4599             gtk_widget_show(image);
4600             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4601         }
4603         {
4604             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between lines"),
4605                                              "tools.text", "line_spacing", 0,
4606                                              us, tbl, FALSE, NULL,
4607                                              -1000.0, 1000.0, 0.1, 0.1,
4608                                              sp_text_line_changed, 0.1, 1);
4609             gtk_widget_set_size_request (hb, 45, 0);
4610             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4611         }
4612     }
4614     {
4615         // horizontal kerning/vertical kerning units menu: create
4616         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4617         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4618         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4620         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4622         // horizontal kerning
4623         {
4624             {
4625                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4626                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4627                 gtk_container_add (GTK_CONTAINER (hb), image);
4628                 gtk_widget_show(image);
4629                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4630             }
4632             {
4633                 GtkWidget *hb = sp_tb_spinbutton((""), _("Horizontal kerning"),
4634                                                  "tools.text", "horizontal_kerning", 0,
4635                                                  us, tbl, FALSE, NULL,
4636                                                  -100.00, 100.00, 0.01, 0.1,
4637                                                  sp_text_horiz_kern_changed);
4638                 gtk_widget_set_size_request (hb, 45, 0);
4639                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4640             }
4641         }
4643         // vertical kerning
4644         {
4645             {
4646                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4647                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4648                 gtk_container_add (GTK_CONTAINER (hb), image);
4649                 gtk_widget_show(image);
4650                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4651             }
4653             {
4654                 GtkWidget *hb = sp_tb_spinbutton((""), _("Vertical kerning"),
4655                                                  "tools.text", "vertical_kerning", 0,
4656                                                  us, tbl, FALSE, NULL,
4657                                                  -100.00, 100.00, 0.01, 0.1,
4658                                                  sp_text_vert_kern_changed);
4659                 gtk_widget_set_size_request (hb, 45, 0);
4660                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4661             }
4662         }
4664         // add the units menu
4665         gtk_widget_show(us);
4666         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4667         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4668     }
4670     // letter rotation
4671     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4672     {
4673         {
4674             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4675             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4676             gtk_container_add (GTK_CONTAINER (hb), image);
4677             gtk_widget_show(image);
4678             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4679         }
4680         {
4681             GtkWidget *hb = sp_tb_spinbutton((""), _("Letter rotation"),
4682                                              "tools.text", "letter_rotation", 0,
4683                                              us, tbl, FALSE, NULL,
4684                                              -180.0, 180.0, 0.1, 0.1,
4685                                              sp_text_letter_rotation_changed, 0.1, 1);
4686             gtk_widget_set_size_request (hb, 45, 0);
4687             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4688         }
4689         // rotation degree label
4690         {
4691             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4692             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4693         }
4694     }
4696     // Remove Manual Kerns
4697     {
4698         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4699         GtkWidget *button = gtk_button_new ();
4700         gtk_container_add (GTK_CONTAINER (button), px);
4701         gtk_widget_show(button);
4702         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4703         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4704         gtk_widget_set_sensitive(button, TRUE);
4705         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4706     }
4707 #endif
4709     gtk_widget_show_all (tbl);
4710     return tbl;
4712 } // end of sp_text_toolbox_new()
4714 }//<unnamed> namespace
4717 //#########################
4718 //##      Connector      ##
4719 //#########################
4721 static void sp_connector_path_set_avoid(void)
4723     cc_selection_set_avoid(true);
4727 static void sp_connector_path_set_ignore(void)
4729     cc_selection_set_avoid(false);
4734 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4736     // quit if run by the _changed callbacks
4737     if (g_object_get_data( tbl, "freeze" )) {
4738         return;
4739     }
4741     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4742     SPDocument *doc = sp_desktop_document(desktop);
4744     if (!sp_document_get_undo_sensitive(doc))
4745     {
4746         return;
4747     }
4749     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4751     if ( repr->attribute("inkscape:connector-spacing") ) {
4752         gdouble priorValue = gtk_adjustment_get_value(adj);
4753         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4754         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4755             return;
4756         }
4757     } else if ( adj->value == defaultConnSpacing ) {
4758         return;
4759     }
4761     // in turn, prevent callbacks from responding
4762     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4764     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4765     SP_OBJECT(desktop->namedview)->updateRepr();
4767     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4768     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4769         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4770         NR::Matrix m = NR::identity();
4771         avoid_item_move(&m, item);
4772     }
4774     if (items) {
4775         g_slist_free(items);
4776     }
4778     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4779             _("Change connector spacing"));
4781     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4783     spinbutton_defocus(GTK_OBJECT(tbl));
4786 static void sp_connector_graph_layout(void)
4788     if (!SP_ACTIVE_DESKTOP) return;
4790     // hack for clones, see comment in align-and-distribute.cpp
4791     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4792     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4794     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4796     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4798     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4801 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4803     if ( gtk_toggle_action_get_active( act ) ) {
4804         prefs_set_string_attribute("tools.connector", "directedlayout",
4805                 "true");
4806     } else {
4807         prefs_set_string_attribute("tools.connector", "directedlayout",
4808                 "false");
4809     }
4812 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4814     if ( gtk_toggle_action_get_active( act ) ) {
4815         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4816                 "true");
4817     } else {
4818         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4819                 "false");
4820     }
4824 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4826     prefs_set_double_attribute("tools.connector", "length", adj->value);
4827     spinbutton_defocus(GTK_OBJECT(tbl));
4830 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4831                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
4832                                             bool /*is_interactive*/, gpointer data)
4834     GtkWidget *tbl = GTK_WIDGET(data);
4836     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4837         return;
4838     }
4839     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4840         return;
4841     }
4843     GtkAdjustment *adj = (GtkAdjustment*)
4844             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4845     gdouble spacing = defaultConnSpacing;
4846     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4848     gtk_adjustment_set_value(adj, spacing);
4852 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4853     NULL, /* child_added */
4854     NULL, /* child_removed */
4855     connector_tb_event_attr_changed,
4856     NULL, /* content_changed */
4857     NULL  /* order_changed */
4858 };
4861 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4863     {
4864         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4865                                           _("Avoid"),
4866                                           _("Make connectors avoid selected objects"),
4867                                           "connector_avoid",
4868                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4869         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
4870         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4871     }
4873     {
4874         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
4875                                           _("Ignore"),
4876                                           _("Make connectors ignore selected objects"),
4877                                           "connector_ignore",
4878                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4879         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
4880         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4881     }
4883     EgeAdjustmentAction* eact = 0;
4885     // Spacing spinbox
4886     eact = create_adjustment_action( "ConnectorSpacingAction",
4887                                      _("Connector Spacing"), _("Spacing:"),
4888                                      _("The amount of space left around objects by auto-routing connectors"),
4889                                      "tools.connector", "spacing", defaultConnSpacing,
4890                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
4891                                      0, 100, 1.0, 10.0,
4892                                      0, 0, 0,
4893                                      connector_spacing_changed, 1, 0 );
4894     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4896     // Graph (connector network) layout
4897     {
4898         InkAction* inky = ink_action_new( "ConnectorGraphAction",
4899                                           _("Graph"),
4900                                           _("Nicely arrange selected connector network"),
4901                                           "graph_layout",
4902                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4903         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
4904         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4905     }
4907     // Default connector length spinbox
4908     eact = create_adjustment_action( "ConnectorLengthAction",
4909                                      _("Connector Length"), _("Length:"),
4910                                      _("Ideal length for connectors when layout is applied"),
4911                                      "tools.connector", "length", 100,
4912                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
4913                                      10, 1000, 10.0, 100.0,
4914                                      0, 0, 0,
4915                                      connector_length_changed, 1, 0 );
4916     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4919     // Directed edges toggle button
4920     {
4921         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
4922                                                       _("Downwards"),
4923                                                       _("Make connectors with end-markers (arrows) point downwards"),
4924                                                       "directed_graph",
4925                                                       Inkscape::ICON_SIZE_DECORATION );
4926         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4928         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
4929         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4930                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4932         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
4933     }
4935     // Avoid overlaps toggle button
4936     {
4937         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
4938                                                       _("Remove overlaps"),
4939                                                       _("Do not allow overlapping shapes"),
4940                                                       "remove_overlaps",
4941                                                       Inkscape::ICON_SIZE_DECORATION );
4942         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4944         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
4945         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4946                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4948         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
4949     }
4951     // Code to watch for changes to the connector-spacing attribute in
4952     // the XML.
4953     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4954     g_assert(repr != NULL);
4956     purge_repr_listener( holder, holder );
4958     if (repr) {
4959         g_object_set_data( holder, "repr", repr );
4960         Inkscape::GC::anchor(repr);
4961         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
4962         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
4963     }
4964 } // end of sp_connector_toolbox_prep()
4967 //#########################
4968 //##     Paintbucket     ##
4969 //#########################
4971 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
4973     gint channels = ege_select_one_action_get_active( act );
4974     flood_channels_set_channels( channels );
4977 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
4979     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4982 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
4984     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
4987 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
4989     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
4990     SPUnit const *unit = tracker->getActiveUnit();
4992     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4994     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
4997 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
4999     // FIXME: make defaults settable via Inkscape Options
5000     struct KeyValue {
5001         char const *key;
5002         double value;
5003     } const key_values[] = {
5004         {"threshold", 15},
5005         {"offset", 0.0}
5006     };
5008     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5009         KeyValue const &kv = key_values[i];
5010         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5011         if ( adj ) {
5012             gtk_adjustment_set_value(adj, kv.value);
5013         }
5014     }
5016     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5017     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5018     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5019     ege_select_one_action_set_active( autogap_action, 0 );
5022 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5024     EgeAdjustmentAction* eact = 0;
5026     {
5027         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5029         GList* items = 0;
5030         gint count = 0;
5031         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5032         {
5033             GtkTreeIter iter;
5034             gtk_list_store_append( model, &iter );
5035             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5036             count++;
5037         }
5038         g_list_free( items );
5039         items = 0;
5040         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5041         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5042         ege_select_one_action_set_appearance( act1, "compact" );
5043         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5044         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5045         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5046         g_object_set_data( holder, "channels_action", act1 );
5047     }
5049     // Spacing spinbox
5050     {
5051         eact = create_adjustment_action(
5052             "ThresholdAction",
5053             _("Fill Threshold"), _("Threshold:"),
5054             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5055             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5056             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5057             0, 0, 0,
5058             paintbucket_threshold_changed, 1, 0 );
5060         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5061     }
5063     // Create the units menu.
5064     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5065     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5066     g_object_set_data( holder, "tracker", tracker );
5067     {
5068         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5069         gtk_action_group_add_action( mainActions, act );
5070     }
5072     // Offset spinbox
5073     {
5074         eact = create_adjustment_action(
5075             "OffsetAction",
5076             _("Grow/shrink by"), _("Grow/shrink by:"),
5077             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5078             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5079             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5080             0, 0, 0,
5081             paintbucket_offset_changed, 1, 2);
5082         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5084         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5085     }
5087     /* Auto Gap */
5088     {
5089         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5091         GList* items = 0;
5092         gint count = 0;
5093         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5094         {
5095             GtkTreeIter iter;
5096             gtk_list_store_append( model, &iter );
5097             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5098             count++;
5099         }
5100         g_list_free( items );
5101         items = 0;
5102         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5103         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5104         ege_select_one_action_set_appearance( act2, "compact" );
5105         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5106         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5107         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5108         g_object_set_data( holder, "autogap_action", act2 );
5109     }
5111     /* Reset */
5112     {
5113         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5114                                           _("Defaults"),
5115                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5116                                           GTK_STOCK_CLEAR );
5117         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5118         gtk_action_group_add_action( mainActions, act );
5119         gtk_action_set_sensitive( act, TRUE );
5120     }
5124 /*
5125   Local Variables:
5126   mode:c++
5127   c-file-style:"stroustrup"
5128   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5129   indent-tabs-mode:nil
5130   fill-column:99
5131   End:
5132 */
5133 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :