Code

76bdc7d07acbba61cdac7d69b57103fa60412f80
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "sp-clippath.h"
78 #include "sp-mask.h"
79 #include "style.h"
80 #include "selection.h"
81 #include "selection-chemistry.h"
82 #include "document-private.h"
83 #include "desktop-style.h"
84 #include "../libnrtype/font-lister.h"
85 #include "../libnrtype/font-instance.h"
86 #include "../connection-pool.h"
87 #include "../prefs-utils.h"
88 #include "../inkscape-stock.h"
89 #include "icon.h"
90 #include "graphlayout/graphlayout.h"
92 #include "mod360.h"
94 #include "toolbox.h"
96 #include "flood-context.h"
98 #include "ink-action.h"
99 #include "ege-adjustment-action.h"
100 #include "ege-output-action.h"
101 #include "ege-select-one-action.h"
102 #include "helper/unit-tracker.h"
104 #include "svg/css-ostringstream.h"
106 #include "widgets/calligraphic-profile-rename.h"
108 using Inkscape::UnitTracker;
110 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
111 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
113 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
126 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
132 static struct {
133     gchar const *type_name;
134     gchar const *data_name;
135     sp_verb_t verb;
136     sp_verb_t doubleclick_verb;
137 } const tools[] = {
138     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
139     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
140     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
141     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
142     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
143     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
144     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
145     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
146     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
147     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
148     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
149     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
150     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
151     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
152     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
153     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
154     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
155     { NULL, NULL, 0, 0 }
156 };
158 static struct {
159     gchar const *type_name;
160     gchar const *data_name;
161     GtkWidget *(*create_func)(SPDesktop *desktop);
162     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
163     gchar const *ui_name;
164     gint swatch_verb_id;
165     gchar const *swatch_tool;
166     gchar const *swatch_tip;
167 } const aux_toolboxes[] = {
168     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
169       SP_VERB_INVALID, 0, 0},
170     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
171       SP_VERB_INVALID, 0, 0},
172     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
173       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
174     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
175       SP_VERB_INVALID, 0, 0},
176     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
177       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
178     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
179       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
180     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
181       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
182     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
183       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
184     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
185       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
186     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
187       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
188     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
189       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
190     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
191       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
192     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
193       SP_VERB_INVALID, 0, 0},
194     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
195       SP_VERB_INVALID, 0, 0},
196     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
197       SP_VERB_INVALID, 0, 0},
198     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
199       SP_VERB_INVALID, 0, 0},
200     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
201       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
202     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
203 };
206 static gchar const * ui_descr =
207         "<ui>"
208         "  <toolbar name='SelectToolbar'>"
209         "    <toolitem action='EditSelectAll' />"
210         "    <toolitem action='EditSelectAllInAllLayers' />"
211         "    <toolitem action='EditDeselect' />"
212         "    <separator />"
213         "    <toolitem action='ObjectRotate90CCW' />"
214         "    <toolitem action='ObjectRotate90' />"
215         "    <toolitem action='ObjectFlipHorizontally' />"
216         "    <toolitem action='ObjectFlipVertically' />"
217         "    <separator />"
218         "    <toolitem action='SelectionToBack' />"
219         "    <toolitem action='SelectionLower' />"
220         "    <toolitem action='SelectionRaise' />"
221         "    <toolitem action='SelectionToFront' />"
222         "    <separator />"
223         "    <toolitem action='XAction' />"
224         "    <toolitem action='YAction' />"
225         "    <toolitem action='WidthAction' />"
226         "    <toolitem action='LockAction' />"
227         "    <toolitem action='HeightAction' />"
228         "    <toolitem action='UnitsAction' />"
229         "    <separator />"
230         "    <toolitem action='transform_affect_label' />"
231         "    <toolitem action='transform_stroke' />"
232         "    <toolitem action='transform_corners' />"
233         "    <toolitem action='transform_gradient' />"
234         "    <toolitem action='transform_pattern' />"
235         "  </toolbar>"
237         "  <toolbar name='NodeToolbar'>"
238         "    <toolitem action='NodeInsertAction' />"
239         "    <toolitem action='NodeDeleteAction' />"
240         "    <separator />"
241         "    <toolitem action='NodeJoinAction' />"
242         "    <toolitem action='NodeBreakAction' />"
243         "    <separator />"
244         "    <toolitem action='NodeJoinSegmentAction' />"
245         "    <toolitem action='NodeDeleteSegmentAction' />"
246         "    <separator />"
247         "    <toolitem action='NodeCuspAction' />"
248         "    <toolitem action='NodeSmoothAction' />"
249         "    <toolitem action='NodeSymmetricAction' />"
250         "    <separator />"
251         "    <toolitem action='NodeLineAction' />"
252         "    <toolitem action='NodeCurveAction' />"
253         "    <separator />"
254         "    <toolitem action='ObjectToPath' />"
255         "    <toolitem action='StrokeToPath' />"
256         "    <separator />"
257         "    <toolitem action='NodeXAction' />"
258         "    <toolitem action='NodeYAction' />"
259         "    <toolitem action='NodeUnitsAction' />"
260         "    <separator />"
261         "    <toolitem action='ObjectEditClipPathAction' />"
262         "    <toolitem action='ObjectEditMaskPathAction' />"
263         "    <toolitem action='EditNextLPEParameterAction' />"
264         "    <separator />"
265         "    <toolitem action='NodesShowHandlesAction' />"
266         "    <toolitem action='NodesShowHelperpath' />"
267         "  </toolbar>"
269         "  <toolbar name='TweakToolbar'>"
270         "    <toolitem action='TweakWidthAction' />"
271         "    <separator />"
272         "    <toolitem action='TweakForceAction' />"
273         "    <toolitem action='TweakPressureAction' />"
274         "    <separator />"
275         "    <toolitem action='TweakModeAction' />"
276         "    <separator />"
277         "    <toolitem action='TweakFidelityAction' />"
278         "    <separator />"
279         "    <toolitem action='TweakChannelsLabel' />"
280         "    <toolitem action='TweakDoH' />"
281         "    <toolitem action='TweakDoS' />"
282         "    <toolitem action='TweakDoL' />"
283         "    <toolitem action='TweakDoO' />"
284         "  </toolbar>"
286         "  <toolbar name='ZoomToolbar'>"
287         "    <toolitem action='ZoomIn' />"
288         "    <toolitem action='ZoomOut' />"
289         "    <separator />"
290         "    <toolitem action='Zoom1:0' />"
291         "    <toolitem action='Zoom1:2' />"
292         "    <toolitem action='Zoom2:1' />"
293         "    <separator />"
294         "    <toolitem action='ZoomSelection' />"
295         "    <toolitem action='ZoomDrawing' />"
296         "    <toolitem action='ZoomPage' />"
297         "    <toolitem action='ZoomPageWidth' />"
298         "    <separator />"
299         "    <toolitem action='ZoomPrev' />"
300         "    <toolitem action='ZoomNext' />"
301         "  </toolbar>"
303         "  <toolbar name='StarToolbar'>"
304         "    <separator />"
305         "    <toolitem action='StarStateAction' />"
306         "    <separator />"
307         "    <toolitem action='FlatAction' />"
308         "    <separator />"
309         "    <toolitem action='MagnitudeAction' />"
310         "    <toolitem action='SpokeAction' />"
311         "    <toolitem action='RoundednessAction' />"
312         "    <toolitem action='RandomizationAction' />"
313         "    <separator />"
314         "    <toolitem action='StarResetAction' />"
315         "  </toolbar>"
317         "  <toolbar name='RectToolbar'>"
318         "    <toolitem action='RectStateAction' />"
319         "    <toolitem action='RectWidthAction' />"
320         "    <toolitem action='RectHeightAction' />"
321         "    <toolitem action='RadiusXAction' />"
322         "    <toolitem action='RadiusYAction' />"
323         "    <toolitem action='RectUnitsAction' />"
324         "    <separator />"
325         "    <toolitem action='RectResetAction' />"
326         "  </toolbar>"
328         "  <toolbar name='3DBoxToolbar'>"
329         "    <toolitem action='3DBoxAngleXAction' />"
330         "    <toolitem action='3DBoxVPXStateAction' />"
331         "    <separator />"
332         "    <toolitem action='3DBoxAngleYAction' />"
333         "    <toolitem action='3DBoxVPYStateAction' />"
334         "    <separator />"
335         "    <toolitem action='3DBoxAngleZAction' />"
336         "    <toolitem action='3DBoxVPZStateAction' />"
337         "  </toolbar>"
339         "  <toolbar name='SpiralToolbar'>"
340         "    <toolitem action='SpiralStateAction' />"
341         "    <toolitem action='SpiralRevolutionAction' />"
342         "    <toolitem action='SpiralExpansionAction' />"
343         "    <toolitem action='SpiralT0Action' />"
344         "    <separator />"
345         "    <toolitem action='SpiralResetAction' />"
346         "  </toolbar>"
348         "  <toolbar name='PenToolbar'>"
349         "  </toolbar>"
351         "  <toolbar name='PencilToolbar'>"
352         "  </toolbar>"
354         "  <toolbar name='CalligraphyToolbar'>"
355         "    <separator />"
356         "    <toolitem action='SetProfileAction'/>"
357         "    <toolitem action='SaveDeleteProfileAction'/>"
358         "    <separator />"
359         "    <toolitem action='CalligraphyWidthAction' />"
360         "    <toolitem action='PressureAction' />"
361         "    <toolitem action='TraceAction' />"
362         "    <toolitem action='ThinningAction' />"
363         "    <separator />"
364         "    <toolitem action='AngleAction' />"
365         "    <toolitem action='TiltAction' />"
366         "    <toolitem action='FixationAction' />"
367         "    <separator />"
368         "    <toolitem action='CapRoundingAction' />"
369         "    <separator />"
370         "    <toolitem action='TremorAction' />"
371         "    <toolitem action='WiggleAction' />"
372         "    <toolitem action='MassAction' />"
373         "    <separator />"
374         "  </toolbar>"
376         "  <toolbar name='ArcToolbar'>"
377         "    <toolitem action='ArcStateAction' />"
378         "    <separator />"
379         "    <toolitem action='ArcStartAction' />"
380         "    <toolitem action='ArcEndAction' />"
381         "    <separator />"
382         "    <toolitem action='ArcOpenAction' />"
383         "    <separator />"
384         "    <toolitem action='ArcResetAction' />"
385         "    <separator />"
386         "  </toolbar>"
388         "  <toolbar name='PaintbucketToolbar'>"
389         "    <toolitem action='ChannelsAction' />"
390         "    <separator />"
391         "    <toolitem action='ThresholdAction' />"
392         "    <separator />"
393         "    <toolitem action='OffsetAction' />"
394         "    <toolitem action='PaintbucketUnitsAction' />"
395         "    <separator />"
396         "    <toolitem action='AutoGapAction' />"
397         "    <separator />"
398         "    <toolitem action='PaintbucketResetAction' />"
399         "  </toolbar>"
401         "  <toolbar name='DropperToolbar'>"
402         "    <toolitem action='DropperOpacityAction' />"
403         "    <toolitem action='DropperPickAlphaAction' />"
404         "    <toolitem action='DropperSetAlphaAction' />"
405         "  </toolbar>"
407         "  <toolbar name='ConnectorToolbar'>"
408         "    <toolitem action='ConnectorAvoidAction' />"
409         "    <toolitem action='ConnectorIgnoreAction' />"
410         "    <toolitem action='ConnectorSpacingAction' />"
411         "    <toolitem action='ConnectorGraphAction' />"
412         "    <toolitem action='ConnectorLengthAction' />"
413         "    <toolitem action='ConnectorDirectedAction' />"
414         "    <toolitem action='ConnectorOverlapAction' />"
415         "  </toolbar>"
417         "</ui>"
420 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
422 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
424 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
425 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
427 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
428 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
430 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
431 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
433 /* Global text entry widgets necessary for update */
434 /* GtkWidget *dropper_rgb_entry,
435           *dropper_opacity_entry ; */
436 // should be made a private member once this is converted to class
438 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
439     connection->disconnect();
440     delete connection;
443 static void purge_repr_listener( GObject* obj, GObject* tbl )
445     (void)obj;
446     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
447     if (oldrepr) { // remove old listener
448         sp_repr_remove_listener_by_data(oldrepr, tbl);
449         Inkscape::GC::release(oldrepr);
450         oldrepr = 0;
451         g_object_set_data( tbl, "repr", NULL );
452     }
455 GtkWidget *
456 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
457                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
458                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
460     SPAction *action = verb->get_action(view);
461     if (!action) return NULL;
463     SPAction *doubleclick_action;
464     if (doubleclick_verb)
465         doubleclick_action = doubleclick_verb->get_action(view);
466     else
467         doubleclick_action = NULL;
469     /* fixme: Handle sensitive/unsensitive */
470     /* fixme: Implement sp_button_new_from_action */
471     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
472     gtk_widget_show(b);
473     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
475     return b;
478 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
479                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
481     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
484 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
485                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
487     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
491 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
493     SPAction* targetAction = SP_ACTION(user_data);
494     if ( targetAction ) {
495         sp_action_perform( targetAction, NULL );
496     }
499 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
501     if ( data ) {
502         GtkAction* act = GTK_ACTION(data);
503         gtk_action_set_sensitive( act, sensitive );
504     }
507 static SPActionEventVector action_event_vector = {
508     {NULL},
509     NULL,
510     NULL,
511     sp_action_action_set_sensitive,
512     NULL,
513     NULL
514 };
516 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
518     GtkAction* act = 0;
520     SPAction* targetAction = verb->get_action(view);
521     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
522     act = GTK_ACTION(inky);
523     gtk_action_set_sensitive( act, targetAction->sensitive );
525     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
527     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
528     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
530     return act;
533 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
535     Inkscape::UI::View::View *view = desktop;
536     gint verbsToUse[] = {
537         // disabled until we have icons for them:
538         //find
539         //SP_VERB_EDIT_TILE,
540         //SP_VERB_EDIT_UNTILE,
541         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
542         SP_VERB_DIALOG_DISPLAY,
543         SP_VERB_DIALOG_FILL_STROKE,
544         SP_VERB_DIALOG_NAMEDVIEW,
545         SP_VERB_DIALOG_TEXT,
546         SP_VERB_DIALOG_XML_EDITOR,
547         SP_VERB_EDIT_CLONE,
548         SP_VERB_EDIT_COPY,
549         SP_VERB_EDIT_CUT,
550         SP_VERB_EDIT_DUPLICATE,
551         SP_VERB_EDIT_PASTE,
552         SP_VERB_EDIT_REDO,
553         SP_VERB_EDIT_UNDO,
554         SP_VERB_EDIT_UNLINK_CLONE,
555         SP_VERB_FILE_EXPORT,
556         SP_VERB_FILE_IMPORT,
557         SP_VERB_FILE_NEW,
558         SP_VERB_FILE_OPEN,
559         SP_VERB_FILE_PRINT,
560         SP_VERB_FILE_SAVE,
561         SP_VERB_OBJECT_TO_CURVE,
562         SP_VERB_SELECTION_GROUP,
563         SP_VERB_SELECTION_OUTLINE,
564         SP_VERB_SELECTION_UNGROUP,
565         SP_VERB_ZOOM_1_1,
566         SP_VERB_ZOOM_1_2,
567         SP_VERB_ZOOM_2_1,
568         SP_VERB_ZOOM_DRAWING,
569         SP_VERB_ZOOM_IN,
570         SP_VERB_ZOOM_NEXT,
571         SP_VERB_ZOOM_OUT,
572         SP_VERB_ZOOM_PAGE,
573         SP_VERB_ZOOM_PAGE_WIDTH,
574         SP_VERB_ZOOM_PREV,
575         SP_VERB_ZOOM_SELECTION,
576     };
578     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
579     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
581     static std::map<SPDesktop*, GtkActionGroup*> groups;
582     GtkActionGroup* mainActions = 0;
583     if ( groups.find(desktop) != groups.end() ) {
584         mainActions = groups[desktop];
585     }
587     if ( !mainActions ) {
588         mainActions = gtk_action_group_new("main");
589         groups[desktop] = mainActions;
590     }
592     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
593         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
594         if ( verb ) {
595             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
596                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
597                 gtk_action_group_add_action( mainActions, act );
598             }
599         }
600     }
602     return mainActions;
606 GtkWidget *
607 sp_tool_toolbox_new()
609     GtkTooltips *tt = gtk_tooltips_new();
610     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
612     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
613     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
615     gtk_widget_set_sensitive(tb, FALSE);
617     GtkWidget *hb = gtk_handle_box_new();
618     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
619     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
620     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
622     gtk_container_add(GTK_CONTAINER(hb), tb);
623     gtk_widget_show(GTK_WIDGET(tb));
625     sigc::connection* conn = new sigc::connection;
626     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
628     return hb;
631 static void
632 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
634     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
635     gtk_widget_queue_resize(child);
638 static void
639 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
641     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
642     gtk_widget_queue_resize(child);
645 GtkWidget *
646 sp_aux_toolbox_new()
648     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
650     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
652     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
654     gtk_widget_set_sensitive(tb, FALSE);
656     GtkWidget *hb = gtk_handle_box_new();
657     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
658     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
659     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
661     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
662     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
664     gtk_container_add(GTK_CONTAINER(hb), tb);
665     gtk_widget_show(GTK_WIDGET(tb));
667     sigc::connection* conn = new sigc::connection;
668     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
670     return hb;
673 //####################################
674 //# Commands Bar
675 //####################################
677 GtkWidget *
678 sp_commands_toolbox_new()
680     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
682     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
684     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
685     gtk_widget_set_sensitive(tb, FALSE);
687     GtkWidget *hb = gtk_handle_box_new();
688     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
689     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
690     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
692     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
693     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
695     gtk_container_add(GTK_CONTAINER(hb), tb);
696     gtk_widget_show(GTK_WIDGET(tb));
698     sigc::connection* conn = new sigc::connection;
699     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
701     return hb;
704 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
705                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
706                                                        gchar const *path, gchar const *data, gdouble def,
707                                                        GtkWidget *focusTarget,
708                                                        GtkWidget *us,
709                                                        GObject *dataKludge,
710                                                        gboolean altx, gchar const *altx_mark,
711                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
712                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
713                                                        void (*callback)(GtkAdjustment *, GObject *),
714                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
716     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
717                                                              lower, upper, step, page, page ) );
718     if (us) {
719         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
720     }
722     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
724     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
725     if ( shortLabel ) {
726         g_object_set( act, "short_label", shortLabel, NULL );
727     }
729     if ( (descrCount > 0) && descrLabels && descrValues ) {
730         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
731     }
733     if ( focusTarget ) {
734         ege_adjustment_action_set_focuswidget( act, focusTarget );
735     }
737     if ( altx && altx_mark ) {
738         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
739     }
741     if ( dataKludge ) {
742         g_object_set_data( dataKludge, data, adj );
743     }
745     // Using a cast just to make sure we pass in the right kind of function pointer
746     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
748     return act;
752 //####################################
753 //# node editing callbacks
754 //####################################
756 /**
757  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
758  */
759 static ShapeEditor *get_current_shape_editor()
761     if (!SP_ACTIVE_DESKTOP) {
762         return NULL;
763     }
765     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
767     if (!SP_IS_NODE_CONTEXT(event_context)) {
768         return NULL;
769     }
771     return SP_NODE_CONTEXT(event_context)->shape_editor;
775 void
776 sp_node_path_edit_add(void)
778     ShapeEditor *shape_editor = get_current_shape_editor();
779     if (shape_editor) shape_editor->add_node();
782 void
783 sp_node_path_edit_delete(void)
785     ShapeEditor *shape_editor = get_current_shape_editor();
786     if (shape_editor) shape_editor->delete_nodes();
789 void
790 sp_node_path_edit_delete_segment(void)
792     ShapeEditor *shape_editor = get_current_shape_editor();
793     if (shape_editor) shape_editor->delete_segment();
796 void
797 sp_node_path_edit_break(void)
799     ShapeEditor *shape_editor = get_current_shape_editor();
800     if (shape_editor) shape_editor->break_at_nodes();
803 void
804 sp_node_path_edit_join(void)
806     ShapeEditor *shape_editor = get_current_shape_editor();
807     if (shape_editor) shape_editor->join_nodes();
810 void
811 sp_node_path_edit_join_segment(void)
813     ShapeEditor *shape_editor = get_current_shape_editor();
814     if (shape_editor) shape_editor->join_segments();
817 void
818 sp_node_path_edit_toline(void)
820     ShapeEditor *shape_editor = get_current_shape_editor();
821     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
824 void
825 sp_node_path_edit_tocurve(void)
827     ShapeEditor *shape_editor = get_current_shape_editor();
828     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
831 void
832 sp_node_path_edit_cusp(void)
834     ShapeEditor *shape_editor = get_current_shape_editor();
835     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
838 void
839 sp_node_path_edit_smooth(void)
841     ShapeEditor *shape_editor = get_current_shape_editor();
842     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
845 void
846 sp_node_path_edit_symmetrical(void)
848     ShapeEditor *shape_editor = get_current_shape_editor();
849     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
852 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
853     bool show = gtk_toggle_action_get_active( act );
854     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
855     ShapeEditor *shape_editor = get_current_shape_editor();
856     if (shape_editor) shape_editor->show_handles(show);
859 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
860     bool show = gtk_toggle_action_get_active( act );
861     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
862     ShapeEditor *shape_editor = get_current_shape_editor();
863     if (shape_editor) shape_editor->show_helperpath(show);
866 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
867     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
870 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
871     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
874 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
875     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
878 /* is called when the node selection is modified */
879 static void
880 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
882     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
883     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
884     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
885     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
887     // quit if run by the attr_changed listener
888     if (g_object_get_data( tbl, "freeze" )) {
889         return;
890     }
892     // in turn, prevent listener from responding
893     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
895     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
896     SPUnit const *unit = tracker->getActiveUnit();
898     ShapeEditor *shape_editor = get_current_shape_editor();
899     if (shape_editor && shape_editor->has_nodepath()) {
900         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
901         int n_selected = 0;
902         if (nodepath) {
903             n_selected = nodepath->numSelected();
904         }
906         if (n_selected == 0) {
907             gtk_action_set_sensitive(xact, FALSE);
908             gtk_action_set_sensitive(yact, FALSE);
909         } else {
910             gtk_action_set_sensitive(xact, TRUE);
911             gtk_action_set_sensitive(yact, TRUE);
912             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
913             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
915             if (n_selected == 1) {
916                 NR::Point sel_node = nodepath->singleSelectedCoords();
917                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
918                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
919                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
920                 }
921             } else {
922                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
923                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
924                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
925                     /* Note: Currently x and y will always have a value, even if the coordinates of the
926                        selected nodes don't coincide (in this case we use the coordinates of the center
927                        of the bounding box). So the entries are never set to zero. */
928                     // FIXME: Maybe we should clear the entry if several nodes are selected
929                     //        instead of providing a kind of average value
930                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
931                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
932                 }
933             }
934         }
935     } else {
936         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
937         gtk_action_set_sensitive(xact, FALSE);
938         gtk_action_set_sensitive(yact, FALSE);
939     }
941     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
944 static void
945 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
947     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
949     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
950     SPUnit const *unit = tracker->getActiveUnit();
952     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
953         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
954     }
956     // quit if run by the attr_changed listener
957     if (g_object_get_data( tbl, "freeze" )) {
958         return;
959     }
961     // in turn, prevent listener from responding
962     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
964     ShapeEditor *shape_editor = get_current_shape_editor();
965     if (shape_editor && shape_editor->has_nodepath()) {
966         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
967         if (!strcmp(value_name, "x")) {
968             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
969         }
970         if (!strcmp(value_name, "y")) {
971             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
972         }
973     }
975     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
978 static void
979 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
981     sp_node_path_value_changed(adj, tbl, "x");
984 static void
985 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
987     sp_node_path_value_changed(adj, tbl, "y");
990 void
991 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
993     {
994     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
995     SPItem *item = selection->singleItem();
996     if (item && SP_IS_SHAPE(item)) {
997        LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(item));
998        if (lpeobj) {
999            gtk_action_set_sensitive(w, TRUE);
1000        } else {
1001            gtk_action_set_sensitive(w, FALSE);
1002        }
1003     } else {
1004        gtk_action_set_sensitive(w, FALSE);
1005     }
1006     }
1008     {
1009     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1010     SPItem *item = selection->singleItem();
1011     if (item && item->clip_ref && item->clip_ref->getObject()) {
1012        gtk_action_set_sensitive(w, TRUE);
1013     } else {
1014        gtk_action_set_sensitive(w, FALSE);
1015     }
1016     }
1018     {
1019     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1020     SPItem *item = selection->singleItem();
1021     if (item && item->mask_ref && item->mask_ref->getObject()) {
1022        gtk_action_set_sensitive(w, TRUE);
1023     } else {
1024        gtk_action_set_sensitive(w, FALSE);
1025     }
1026     }
1029 void
1030 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1032     sp_node_toolbox_sel_changed (selection, tbl);
1037 //################################
1038 //##    Node Editing Toolbox    ##
1039 //################################
1041 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1043     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1044     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1045     g_object_set_data( holder, "tracker", tracker );
1047     {
1048         InkAction* inky = ink_action_new( "NodeInsertAction",
1049                                           _("Insert node"),
1050                                           _("Insert new nodes into selected segments"),
1051                                           "node_insert",
1052                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1053         g_object_set( inky, "short_label", _("Insert"), NULL );
1054         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1055         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1056     }
1058     {
1059         InkAction* inky = ink_action_new( "NodeDeleteAction",
1060                                           _("Delete node"),
1061                                           _("Delete selected nodes"),
1062                                           "node_delete",
1063                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1064         g_object_set( inky, "short_label", _("Delete"), NULL );
1065         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1066         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1067     }
1069     {
1070         InkAction* inky = ink_action_new( "NodeJoinAction",
1071                                           _("Join endnodes"),
1072                                           _("Join selected endnodes"),
1073                                           "node_join",
1074                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1075         g_object_set( inky, "short_label", _("Join"), NULL );
1076         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1077         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1078     }
1080     {
1081         InkAction* inky = ink_action_new( "NodeBreakAction",
1082                                           _("Break nodes"),
1083                                           _("Break path at selected nodes"),
1084                                           "node_break",
1085                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1086         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1087         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1088     }
1091     {
1092         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1093                                           _("Join with segment"),
1094                                           _("Join selected endnodes with a new segment"),
1095                                           "node_join_segment",
1096                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1097         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1098         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1099     }
1101     {
1102         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1103                                           _("Delete segment"),
1104                                           _("Delete segment between two non-endpoint nodes"),
1105                                           "node_delete_segment",
1106                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1107         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1108         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1109     }
1111     {
1112         InkAction* inky = ink_action_new( "NodeCuspAction",
1113                                           _("Node Cusp"),
1114                                           _("Make selected nodes corner"),
1115                                           "node_cusp",
1116                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1117         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1118         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1119     }
1121     {
1122         InkAction* inky = ink_action_new( "NodeSmoothAction",
1123                                           _("Node Smooth"),
1124                                           _("Make selected nodes smooth"),
1125                                           "node_smooth",
1126                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1127         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1128         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1129     }
1131     {
1132         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1133                                           _("Node Symmetric"),
1134                                           _("Make selected nodes symmetric"),
1135                                           "node_symmetric",
1136                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1137         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1138         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1139     }
1141     {
1142         InkAction* inky = ink_action_new( "NodeLineAction",
1143                                           _("Node Line"),
1144                                           _("Make selected segments lines"),
1145                                           "node_line",
1146                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1147         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1148         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1149     }
1151     {
1152         InkAction* inky = ink_action_new( "NodeCurveAction",
1153                                           _("Node Curve"),
1154                                           _("Make selected segments curves"),
1155                                           "node_curve",
1156                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1157         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1158         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1159     }
1161     {
1162         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1163                                                       _("Show Handles"),
1164                                                       _("Show the Bezier handles of selected nodes"),
1165                                                       "nodes_show_handles",
1166                                                       Inkscape::ICON_SIZE_DECORATION );
1167         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1168         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1169         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1170     }
1172     {
1173         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1174                                                       _("Show Outline"),
1175                                                       _("Show the outline of the path"),
1176                                                       "nodes_show_helperpath",
1177                                                       Inkscape::ICON_SIZE_DECORATION );
1178         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1179         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1180         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1181     }
1183     {
1184         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1185                                           _("Next path effect parameter"),
1186                                           _("Show next path effect parameter for editing"),
1187                                           "edit_next_parameter",
1188                                           Inkscape::ICON_SIZE_DECORATION );
1189         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1190         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1191         g_object_set_data( holder, "nodes_lpeedit", inky);
1192     }
1194     {
1195         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1196                                           _("Edit clipping path"),
1197                                           _("Edit the clipping path of the object"),
1198                                           "nodeedit-clippath",
1199                                           Inkscape::ICON_SIZE_DECORATION );
1200         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1201         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1202         g_object_set_data( holder, "nodes_clippathedit", inky);
1203     }
1205     {
1206         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1207                                           _("Edit mask path"),
1208                                           _("Edit the mask of the object"),
1209                                           "nodeedit-mask",
1210                                           Inkscape::ICON_SIZE_DECORATION );
1211         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1212         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1213         g_object_set_data( holder, "nodes_maskedit", inky);
1214     }
1216     /* X coord of selected node(s) */
1217     {
1218         EgeAdjustmentAction* eact = 0;
1219         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1220         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1221         eact = create_adjustment_action( "NodeXAction",
1222                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1223                                          "tools.nodes", "Xcoord", 0,
1224                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1225                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1226                                          labels, values, G_N_ELEMENTS(labels),
1227                                          sp_node_path_x_value_changed );
1228         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1229         g_object_set_data( holder, "nodes_x_action", eact );
1230         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1231         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1232     }
1234     /* Y coord of selected node(s) */
1235     {
1236         EgeAdjustmentAction* eact = 0;
1237         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1238         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1239         eact = create_adjustment_action( "NodeYAction",
1240                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1241                                          "tools.nodes", "Ycoord", 0,
1242                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1243                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1244                                          labels, values, G_N_ELEMENTS(labels),
1245                                          sp_node_path_y_value_changed );
1246         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1247         g_object_set_data( holder, "nodes_y_action", eact );
1248         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1249         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1250     }
1252     // add the units menu
1253     {
1254         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1255         gtk_action_group_add_action( mainActions, act );
1256     }
1259     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1261     //watch selection
1262     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1264     sigc::connection *c_selection_changed =
1265         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1266                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1267     pool->add_connection ("selection-changed", c_selection_changed);
1269     sigc::connection *c_selection_modified =
1270         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1271                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1272     pool->add_connection ("selection-modified", c_selection_modified);
1274     sigc::connection *c_subselection_changed =
1275         new sigc::connection (desktop->connectToolSubselectionChanged
1276                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1277     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1279     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1281     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1282 } // end of sp_node_toolbox_prep()
1285 //########################
1286 //##    Zoom Toolbox    ##
1287 //########################
1289 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1291     // no custom GtkAction setup needed
1292 } // end of sp_zoom_toolbox_prep()
1294 void
1295 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1297     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")));
1301 void
1302 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1304     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")));
1307 void
1308 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1310     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")));
1313 static void
1314 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1316     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1317     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1319     if (old_desktop) {
1320         GList *children, *iter;
1322         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1323         for ( iter = children ; iter ; iter = iter->next ) {
1324             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1325         }
1326         g_list_free(children);
1327     }
1329     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1331     if (desktop) {
1332         gtk_widget_set_sensitive(toolbox, TRUE);
1333         setup_func(toolbox, desktop);
1334         update_func(desktop, desktop->event_context, toolbox);
1335         *conn = desktop->connectEventContextChanged
1336             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1337     } else {
1338         gtk_widget_set_sensitive(toolbox, FALSE);
1339     }
1341 } // end of toolbox_set_desktop()
1344 static void
1345 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1347     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1348     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1349     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1350         // "toolbox.tools" was not set. Fallback to older value
1351         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1353         // Copy the setting forwards
1354         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1355     }
1356     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1358     for (int i = 0 ; tools[i].type_name ; i++ ) {
1359         GtkWidget *button =
1360             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1361                                                               SP_BUTTON_TYPE_TOGGLE,
1362                                                               Inkscape::Verb::get(tools[i].verb),
1363                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1364                                                               desktop,
1365                                                               tooltips );
1367         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1368                            (gpointer)button );
1369     }
1373 static void
1374 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1376     gchar const *const tname = ( eventcontext
1377                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1378                                  : NULL );
1379     for (int i = 0 ; tools[i].type_name ; i++ ) {
1380         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1381         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1382     }
1385 static void
1386 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1388     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1389     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1390     GtkUIManager* mgr = gtk_ui_manager_new();
1391     GError* errVal = 0;
1392     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1393     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1395     std::map<std::string, GtkWidget*> dataHolders;
1397     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1398         if ( aux_toolboxes[i].prep_func ) {
1399             // converted to GtkActions and UIManager
1401             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1402             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1403             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1404             dataHolders[aux_toolboxes[i].type_name] = kludge;
1405             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1406         } else {
1408             GtkWidget *sub_toolbox = 0;
1409             if (aux_toolboxes[i].create_func == NULL)
1410                 sub_toolbox = sp_empty_toolbox_new(desktop);
1411             else {
1412                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1413             }
1415             gtk_size_group_add_widget( grouper, sub_toolbox );
1417             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1418             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1420         }
1421     }
1423     // Second pass to create toolbars *after* all GtkActions are created
1424     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1425         if ( aux_toolboxes[i].prep_func ) {
1426             // converted to GtkActions and UIManager
1428             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1430             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1431             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1433             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1434             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1435             g_free( tmp );
1436             tmp = 0;
1438             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1439             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1440             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1441                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1442             }
1443             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1446             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1448             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1449                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1450                 swatch->setDesktop( desktop );
1451                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1452                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1453                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1454                 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 );
1455             }
1457             gtk_widget_show_all( holder );
1458             sp_set_font_size_smaller( holder );
1460             gtk_size_group_add_widget( grouper, holder );
1462             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1463             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1464         }
1465     }
1467     g_object_unref( G_OBJECT(grouper) );
1470 static void
1471 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1473     gchar const *tname = ( eventcontext
1474                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1475                            : NULL );
1476     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1477         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1478         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1479             gtk_widget_show_all(sub_toolbox);
1480             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1481         } else {
1482             gtk_widget_hide(sub_toolbox);
1483         }
1484     }
1487 static void
1488 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1490     gchar const * descr =
1491         "<ui>"
1492         "  <toolbar name='CommandsToolbar'>"
1493         "    <toolitem action='FileNew' />"
1494         "    <toolitem action='FileOpen' />"
1495         "    <toolitem action='FileSave' />"
1496         "    <toolitem action='FilePrint' />"
1497         "    <separator />"
1498         "    <toolitem action='FileImport' />"
1499         "    <toolitem action='FileExport' />"
1500         "    <separator />"
1501         "    <toolitem action='EditUndo' />"
1502         "    <toolitem action='EditRedo' />"
1503         "    <separator />"
1504         "    <toolitem action='EditCopy' />"
1505         "    <toolitem action='EditCut' />"
1506         "    <toolitem action='EditPaste' />"
1507         "    <separator />"
1508         "    <toolitem action='ZoomSelection' />"
1509         "    <toolitem action='ZoomDrawing' />"
1510         "    <toolitem action='ZoomPage' />"
1511         "    <separator />"
1512         "    <toolitem action='EditDuplicate' />"
1513         "    <toolitem action='EditClone' />"
1514         "    <toolitem action='EditUnlinkClone' />"
1515         "    <separator />"
1516         "    <toolitem action='SelectionGroup' />"
1517         "    <toolitem action='SelectionUnGroup' />"
1518         "    <separator />"
1519         "    <toolitem action='DialogFillStroke' />"
1520         "    <toolitem action='DialogText' />"
1521         "    <toolitem action='DialogXMLEditor' />"
1522         "    <toolitem action='DialogAlignDistribute' />"
1523         "    <separator />"
1524         "    <toolitem action='DialogPreferences' />"
1525         "    <toolitem action='DialogDocumentProperties' />"
1526         "  </toolbar>"
1527         "</ui>";
1528     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1531     GtkUIManager* mgr = gtk_ui_manager_new();
1532     GError* errVal = 0;
1534     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1535     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1537     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1538     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1539         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1540     }
1541     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1542     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1543     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1546     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1549 static void
1550 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1554 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1556     gtk_widget_show(toolbox_toplevel);
1557     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1559     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1560     if (!shown_toolbox) {
1561         return;
1562     }
1563     gtk_widget_show(toolbox);
1565     gtk_widget_show_all(shown_toolbox);
1568 void
1569 aux_toolbox_space(GtkWidget *tb, gint space)
1571     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1574 static GtkWidget *
1575 sp_empty_toolbox_new(SPDesktop *desktop)
1577     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1578     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1579     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1581     gtk_widget_show_all(tbl);
1582     sp_set_font_size_smaller (tbl);
1584     return tbl;
1587 // helper UI functions
1589 GtkWidget *
1590 sp_tb_spinbutton(
1591     gchar *label, gchar const *tooltip,
1592     gchar const *path, gchar const *data, gdouble def,
1593     GtkWidget *us,
1594     GtkWidget *tbl,
1595     gboolean altx, gchar const *altx_mark,
1596     gdouble lower, gdouble upper, gdouble step, gdouble page,
1597     void (*callback)(GtkAdjustment *, GtkWidget *),
1598     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1600     GtkTooltips *tt = gtk_tooltips_new();
1602     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1604     GtkWidget *l = gtk_label_new(label);
1605     gtk_widget_show(l);
1606     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1607     gtk_container_add(GTK_CONTAINER(hb), l);
1609     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1610                                       lower, upper, step, page, page);
1611     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1612     if (us)
1613         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1615     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1616     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1617     if (altx)
1618         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1619     gtk_widget_set_size_request(sb,
1620                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1621                                 AUX_SPINBUTTON_HEIGHT);
1622     gtk_widget_show(sb);
1623     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1624     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1625     gtk_container_add(GTK_CONTAINER(hb), sb);
1626     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1628     return hb;
1631 #define MODE_LABEL_WIDTH 70
1633 //########################
1634 //##       Star         ##
1635 //########################
1637 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1639     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1641     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1642         // do not remember prefs if this call is initiated by an undo change, because undoing object
1643         // creation sets bogus values to its attributes before it is deleted
1644         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1645     }
1647     // quit if run by the attr_changed listener
1648     if (g_object_get_data( dataKludge, "freeze" )) {
1649         return;
1650     }
1652     // in turn, prevent listener from responding
1653     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1655     bool modmade = false;
1657     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1658     GSList const *items = selection->itemList();
1659     for (; items != NULL; items = items->next) {
1660         if (SP_IS_STAR((SPItem *) items->data)) {
1661             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1662             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1663             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1664                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1665                                     + M_PI / (gint)adj->value));
1666             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1667             modmade = true;
1668         }
1669     }
1670     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1671                                    _("Star: Change number of corners"));
1673     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1676 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1678     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1680     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1681         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1682     }
1684     // quit if run by the attr_changed listener
1685     if (g_object_get_data( dataKludge, "freeze" )) {
1686         return;
1687     }
1689     // in turn, prevent listener from responding
1690     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1692     bool modmade = false;
1693     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1694     GSList const *items = selection->itemList();
1695     for (; items != NULL; items = items->next) {
1696         if (SP_IS_STAR((SPItem *) items->data)) {
1697             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1699             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1700             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1701             if (r2 < r1) {
1702                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1703             } else {
1704                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1705             }
1707             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1708             modmade = true;
1709         }
1710     }
1712     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1713                                    _("Star: Change spoke ratio"));
1715     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1718 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1720     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1721     bool flat = ege_select_one_action_get_active( act ) == 0;
1723     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1724         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1725                                     flat ? "true" : "false" );
1726     }
1728     // quit if run by the attr_changed listener
1729     if (g_object_get_data( dataKludge, "freeze" )) {
1730         return;
1731     }
1733     // in turn, prevent listener from responding
1734     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1736     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1737     GSList const *items = selection->itemList();
1738     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1739     bool modmade = false;
1741     if ( prop_action ) {
1742         gtk_action_set_sensitive( prop_action, !flat );
1743     }
1745     for (; items != NULL; items = items->next) {
1746         if (SP_IS_STAR((SPItem *) items->data)) {
1747             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1748             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1749             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1750             modmade = true;
1751         }
1752     }
1754     if (modmade) {
1755         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1756                          flat ? _("Make polygon") : _("Make star"));
1757     }
1759     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1762 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1764     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1766     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1767         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1768     }
1770     // quit if run by the attr_changed listener
1771     if (g_object_get_data( dataKludge, "freeze" )) {
1772         return;
1773     }
1775     // in turn, prevent listener from responding
1776     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1778     bool modmade = false;
1780     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1781     GSList const *items = selection->itemList();
1782     for (; items != NULL; items = items->next) {
1783         if (SP_IS_STAR((SPItem *) items->data)) {
1784             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1785             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1786             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1787             modmade = true;
1788         }
1789     }
1790     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1791                                    _("Star: Change rounding"));
1793     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1796 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1798     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1800     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1801         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1802     }
1804     // quit if run by the attr_changed listener
1805     if (g_object_get_data( dataKludge, "freeze" )) {
1806         return;
1807     }
1809     // in turn, prevent listener from responding
1810     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1812     bool modmade = false;
1814     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1815     GSList const *items = selection->itemList();
1816     for (; items != NULL; items = items->next) {
1817         if (SP_IS_STAR((SPItem *) items->data)) {
1818             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1819             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1820             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1821             modmade = true;
1822         }
1823     }
1824     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1825                                    _("Star: Change randomization"));
1827     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1831 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1832                                        gchar const */*old_value*/, gchar const */*new_value*/,
1833                                        bool /*is_interactive*/, gpointer data)
1835     GtkWidget *tbl = GTK_WIDGET(data);
1837     // quit if run by the _changed callbacks
1838     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1839         return;
1840     }
1842     // in turn, prevent callbacks from responding
1843     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1845     GtkAdjustment *adj = 0;
1847     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1848     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1850     if (!strcmp(name, "inkscape:randomized")) {
1851         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1852         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1853     } else if (!strcmp(name, "inkscape:rounded")) {
1854         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1855         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1856     } else if (!strcmp(name, "inkscape:flatsided")) {
1857         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1858         char const *flatsides = repr->attribute("inkscape:flatsided");
1859         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1860         if ( flatsides && !strcmp(flatsides,"false") ) {
1861             ege_select_one_action_set_active( flat_action, 1 );
1862             gtk_action_set_sensitive( prop_action, TRUE );
1863         } else {
1864             ege_select_one_action_set_active( flat_action, 0 );
1865             gtk_action_set_sensitive( prop_action, FALSE );
1866         }
1867     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1868         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1869         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1870         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1871         if (r2 < r1) {
1872             gtk_adjustment_set_value(adj, r2/r1);
1873         } else {
1874             gtk_adjustment_set_value(adj, r1/r2);
1875         }
1876     } else if (!strcmp(name, "sodipodi:sides")) {
1877         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1878         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1879     }
1881     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1885 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1887     NULL, /* child_added */
1888     NULL, /* child_removed */
1889     star_tb_event_attr_changed,
1890     NULL, /* content_changed */
1891     NULL  /* order_changed */
1892 };
1895 /**
1896  *  \param selection Should not be NULL.
1897  */
1898 static void
1899 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1901     int n_selected = 0;
1902     Inkscape::XML::Node *repr = NULL;
1904     purge_repr_listener( tbl, tbl );
1906     for (GSList const *items = selection->itemList();
1907          items != NULL;
1908          items = items->next)
1909     {
1910         if (SP_IS_STAR((SPItem *) items->data)) {
1911             n_selected++;
1912             repr = SP_OBJECT_REPR((SPItem *) items->data);
1913         }
1914     }
1916     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1918     if (n_selected == 0) {
1919         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1920     } else if (n_selected == 1) {
1921         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1923         if (repr) {
1924             g_object_set_data( tbl, "repr", repr );
1925             Inkscape::GC::anchor(repr);
1926             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1927             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1928         }
1929     } else {
1930         // FIXME: implement averaging of all parameters for multiple selected stars
1931         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1932         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1933     }
1937 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1939     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1940     // callbacks to lump all the changes for all selected objects in one undo step
1942     GtkAdjustment *adj = 0;
1944     // fixme: make settable in prefs!
1945     gint mag = 5;
1946     gdouble prop = 0.5;
1947     gboolean flat = FALSE;
1948     gdouble randomized = 0;
1949     gdouble rounded = 0;
1951     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1952     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1954     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1955     gtk_action_set_sensitive( sb2, !flat );
1957     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1958     gtk_adjustment_set_value(adj, mag);
1959     gtk_adjustment_value_changed(adj);
1961     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1962     gtk_adjustment_set_value(adj, prop);
1963     gtk_adjustment_value_changed(adj);
1965     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1966     gtk_adjustment_set_value(adj, rounded);
1967     gtk_adjustment_value_changed(adj);
1969     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1970     gtk_adjustment_set_value(adj, randomized);
1971     gtk_adjustment_value_changed(adj);
1975 void
1976 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1978     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1979     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1980     GtkWidget *l = gtk_label_new(NULL);
1981     gtk_label_set_markup(GTK_LABEL(l), title);
1982     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1983     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1984     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1988 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1990     {
1991         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1992         ege_output_action_set_use_markup( act, TRUE );
1993         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1994         g_object_set_data( holder, "mode_action", act );
1995     }
1997     {
1998         EgeAdjustmentAction* eact = 0;
1999         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2000         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2002         /* Flatsided checkbox */
2003         {
2004             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2006             GtkTreeIter iter;
2007             gtk_list_store_append( model, &iter );
2008             gtk_list_store_set( model, &iter,
2009                                 0, _("Polygon"),
2010                                 1, _("Regular polygon (with one handle) instead of a star"),
2011                                 2, "star_flat",
2012                                 -1 );
2014             gtk_list_store_append( model, &iter );
2015             gtk_list_store_set( model, &iter,
2016                                 0, _("Star"),
2017                                 1, _("Star instead of a regular polygon (with one handle)"),
2018                                 2, "star_angled",
2019                                 -1 );
2021             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2022             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2023             g_object_set_data( holder, "flat_action", act );
2025             ege_select_one_action_set_appearance( act, "full" );
2026             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2027             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2028             ege_select_one_action_set_icon_column( act, 2 );
2029             ege_select_one_action_set_tooltip_column( act, 1  );
2031             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2032             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2033         }
2035         /* Magnitude */
2036         {
2037         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2038         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2039         eact = create_adjustment_action( "MagnitudeAction",
2040                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2041                                          "tools.shapes.star", "magnitude", 3,
2042                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2043                                          3, 1024, 1, 5,
2044                                          labels, values, G_N_ELEMENTS(labels),
2045                                          sp_stb_magnitude_value_changed,
2046                                          1.0, 0 );
2047         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2048         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2049         }
2051         /* Spoke ratio */
2052         {
2053         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2054         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2055         eact = create_adjustment_action( "SpokeAction",
2056                                          _("Spoke ratio"), _("Spoke ratio:"),
2057                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2058                                          // Base radius is the same for the closest handle.
2059                                          _("Base radius to tip radius ratio"),
2060                                          "tools.shapes.star", "proportion", 0.5,
2061                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2062                                          0.01, 1.0, 0.01, 0.1,
2063                                          labels, values, G_N_ELEMENTS(labels),
2064                                          sp_stb_proportion_value_changed );
2065         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2066         g_object_set_data( holder, "prop_action", eact );
2067         }
2069         if ( !isFlatSided ) {
2070             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2071         } else {
2072             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2073         }
2075         /* Roundedness */
2076         {
2077         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2078         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2079         eact = create_adjustment_action( "RoundednessAction",
2080                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2081                                          "tools.shapes.star", "rounded", 0.0,
2082                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2083                                          -10.0, 10.0, 0.01, 0.1,
2084                                          labels, values, G_N_ELEMENTS(labels),
2085                                          sp_stb_rounded_value_changed );
2086         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2087         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2088         }
2090         /* Randomization */
2091         {
2092         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2093         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2094         eact = create_adjustment_action( "RandomizationAction",
2095                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2096                                          "tools.shapes.star", "randomized", 0.0,
2097                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2098                                          -10.0, 10.0, 0.001, 0.01,
2099                                          labels, values, G_N_ELEMENTS(labels),
2100                                          sp_stb_randomized_value_changed, 0.1, 3 );
2101         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2102         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2103         }
2104     }
2106     {
2107         /* Reset */
2108         {
2109             GtkAction* act = gtk_action_new( "StarResetAction",
2110                                              _("Defaults"),
2111                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2112                                              GTK_STOCK_CLEAR );
2113             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2114             gtk_action_group_add_action( mainActions, act );
2115             gtk_action_set_sensitive( act, TRUE );
2116         }
2117     }
2119     sigc::connection *connection = new sigc::connection(
2120         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2121         );
2122     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2123     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2127 //########################
2128 //##       Rect         ##
2129 //########################
2131 static void sp_rtb_sensitivize( GObject *tbl )
2133     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2134     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2135     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2137     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2138         gtk_action_set_sensitive( not_rounded, FALSE );
2139     } else {
2140         gtk_action_set_sensitive( not_rounded, TRUE );
2141     }
2145 static void
2146 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2147                           void (*setter)(SPRect *, gdouble))
2149     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2151     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2152     SPUnit const *unit = tracker->getActiveUnit();
2154     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2155         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2156     }
2158     // quit if run by the attr_changed listener
2159     if (g_object_get_data( tbl, "freeze" )) {
2160         return;
2161     }
2163     // in turn, prevent listener from responding
2164     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2166     bool modmade = false;
2167     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2168     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2169         if (SP_IS_RECT(items->data)) {
2170             if (adj->value != 0) {
2171                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2172             } else {
2173                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2174             }
2175             modmade = true;
2176         }
2177     }
2179     sp_rtb_sensitivize( tbl );
2181     if (modmade) {
2182         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2183                                    _("Change rectangle"));
2184     }
2186     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2189 static void
2190 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2192     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2195 static void
2196 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2198     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2201 static void
2202 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2204     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2207 static void
2208 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2210     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2215 static void
2216 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2218     GtkAdjustment *adj = 0;
2220     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2221     gtk_adjustment_set_value(adj, 0.0);
2222     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2223     gtk_adjustment_value_changed(adj);
2225     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2226     gtk_adjustment_set_value(adj, 0.0);
2227     gtk_adjustment_value_changed(adj);
2229     sp_rtb_sensitivize( obj );
2232 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2233                                        gchar const */*old_value*/, gchar const */*new_value*/,
2234                                        bool /*is_interactive*/, gpointer data)
2236     GObject *tbl = G_OBJECT(data);
2238     // quit if run by the _changed callbacks
2239     if (g_object_get_data( tbl, "freeze" )) {
2240         return;
2241     }
2243     // in turn, prevent callbacks from responding
2244     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2246     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2247     SPUnit const *unit = tracker->getActiveUnit();
2249     gpointer item = g_object_get_data( tbl, "item" );
2250     if (item && SP_IS_RECT(item)) {
2251         {
2252             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2253             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2254             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2255         }
2257         {
2258             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2259             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2260             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2261         }
2263         {
2264             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2265             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2266             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2267         }
2269         {
2270             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2271             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2272             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2273         }
2274     }
2276     sp_rtb_sensitivize( tbl );
2278     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2282 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2283     NULL, /* child_added */
2284     NULL, /* child_removed */
2285     rect_tb_event_attr_changed,
2286     NULL, /* content_changed */
2287     NULL  /* order_changed */
2288 };
2290 /**
2291  *  \param selection should not be NULL.
2292  */
2293 static void
2294 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2296     int n_selected = 0;
2297     Inkscape::XML::Node *repr = NULL;
2298     SPItem *item = NULL;
2300     if ( g_object_get_data( tbl, "repr" ) ) {
2301         g_object_set_data( tbl, "item", NULL );
2302     }
2303     purge_repr_listener( tbl, tbl );
2305     for (GSList const *items = selection->itemList();
2306          items != NULL;
2307          items = items->next) {
2308         if (SP_IS_RECT((SPItem *) items->data)) {
2309             n_selected++;
2310             item = (SPItem *) items->data;
2311             repr = SP_OBJECT_REPR(item);
2312         }
2313     }
2315     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2317     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2319     if (n_selected == 0) {
2320         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2322         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2323         gtk_action_set_sensitive(w, FALSE);
2324         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2325         gtk_action_set_sensitive(h, FALSE);
2327     } else if (n_selected == 1) {
2328         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2329         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2331         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2332         gtk_action_set_sensitive(w, TRUE);
2333         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2334         gtk_action_set_sensitive(h, TRUE);
2336         if (repr) {
2337             g_object_set_data( tbl, "repr", repr );
2338             g_object_set_data( tbl, "item", item );
2339             Inkscape::GC::anchor(repr);
2340             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2341             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2342         }
2343     } else {
2344         // FIXME: implement averaging of all parameters for multiple selected
2345         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2346         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2347         sp_rtb_sensitivize( tbl );
2348     }
2352 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2354     EgeAdjustmentAction* eact = 0;
2356     {
2357         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2358         ege_output_action_set_use_markup( act, TRUE );
2359         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2360         g_object_set_data( holder, "mode_action", act );
2361     }
2363     // rx/ry units menu: create
2364     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2365     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2366     // fixme: add % meaning per cent of the width/height
2367     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2368     g_object_set_data( holder, "tracker", tracker );
2370     /* W */
2371     {
2372         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2373         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2374         eact = create_adjustment_action( "RectWidthAction",
2375                                          _("Width"), _("W:"), _("Width of rectangle"),
2376                                          "tools.shapes.rect", "width", 0,
2377                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2378                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2379                                          labels, values, G_N_ELEMENTS(labels),
2380                                          sp_rtb_width_value_changed );
2381         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2382         g_object_set_data( holder, "width_action", eact );
2383         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2384         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2385     }
2387     /* H */
2388     {
2389         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2390         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2391         eact = create_adjustment_action( "RectHeightAction",
2392                                          _("Height"), _("H:"), _("Height of rectangle"),
2393                                          "tools.shapes.rect", "height", 0,
2394                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2395                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2396                                          labels, values, G_N_ELEMENTS(labels),
2397                                          sp_rtb_height_value_changed );
2398         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2399         g_object_set_data( holder, "height_action", eact );
2400         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2401         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2402     }
2404     /* rx */
2405     {
2406         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2407         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2408         eact = create_adjustment_action( "RadiusXAction",
2409                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2410                                          "tools.shapes.rect", "rx", 0,
2411                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2412                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2413                                          labels, values, G_N_ELEMENTS(labels),
2414                                          sp_rtb_rx_value_changed);
2415         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2416         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2417     }
2419     /* ry */
2420     {
2421         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2422         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2423         eact = create_adjustment_action( "RadiusYAction",
2424                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2425                                          "tools.shapes.rect", "ry", 0,
2426                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2427                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2428                                          labels, values, G_N_ELEMENTS(labels),
2429                                          sp_rtb_ry_value_changed);
2430         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2431         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2432     }
2434     // add the units menu
2435     {
2436         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2437         gtk_action_group_add_action( mainActions, act );
2438     }
2440     /* Reset */
2441     {
2442         InkAction* inky = ink_action_new( "RectResetAction",
2443                                           _("Not rounded"),
2444                                           _("Make corners sharp"),
2445                                           "squared_corner",
2446                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2447         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2448         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2449         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2450         g_object_set_data( holder, "not_rounded", inky );
2451     }
2453     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2454     sp_rtb_sensitivize( holder );
2456     sigc::connection *connection = new sigc::connection(
2457         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2458         );
2459     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2460     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2463 //########################
2464 //##       3D Box       ##
2465 //########################
2467 // normalize angle so that it lies in the interval [0,360]
2468 static double box3d_normalize_angle (double a) {
2469     double angle = a + ((int) (a/360.0))*360;
2470     if (angle < 0) {
2471         angle += 360.0;
2472     }
2473     return angle;
2476 static void
2477 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2478                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2479     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2480     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2481     //       are reset).
2482     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2484     if (is_infinite) {
2485         gtk_toggle_action_set_active(tact, TRUE);
2486         gtk_action_set_sensitive(act, TRUE);
2488         double angle = persp3d_get_infinite_angle(persp, axis);
2489         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2490             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2491         }
2492     } else {
2493         gtk_toggle_action_set_active(tact, FALSE);
2494         gtk_action_set_sensitive(act, FALSE);
2495     }
2498 static void
2499 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2500     if (!persp_repr) {
2501         g_print ("No perspective given to box3d_resync_toolbar().\n");
2502         return;
2503     }
2505     GtkWidget *tbl = GTK_WIDGET(data);
2506     GtkAdjustment *adj = 0;
2507     GtkAction *act = 0;
2508     GtkToggleAction *tact = 0;
2509     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2510     {
2511         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2512         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2513         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2515         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2516     }
2517     {
2518         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2519         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2520         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2522         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2523     }
2524     {
2525         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2526         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2527         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2529         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2530     }
2533 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2534                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2535                                                   bool /*is_interactive*/, gpointer data)
2537     GtkWidget *tbl = GTK_WIDGET(data);
2539     // quit if run by the attr_changed listener
2540     // note: it used to work without the differently called freeze_ attributes (here and in
2541     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2542     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2543         return;
2544     }
2546     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2547     // sp_document_maybe_done() when the document is undo insensitive)
2548     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2550     // TODO: Only update the appropriate part of the toolbar
2551 //    if (!strcmp(name, "inkscape:vp_z")) {
2552         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2553 //    }
2555     Persp3D *persp = persp3d_get_from_repr(repr);
2556     persp3d_update_box_reprs(persp);
2558     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2561 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2563     NULL, /* child_added */
2564     NULL, /* child_removed */
2565     box3d_persp_tb_event_attr_changed,
2566     NULL, /* content_changed */
2567     NULL  /* order_changed */
2568 };
2570 /**
2571  *  \param selection Should not be NULL.
2572  */
2573 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2574 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2575 static void
2576 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2578     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2579     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2580     // update the perspectives with infinite VPs and leave the other ones untouched).
2582     Inkscape::XML::Node *persp_repr = NULL;
2583     purge_repr_listener(tbl, tbl);
2585     SPItem *item = selection->singleItem();
2586     if (item && SP_IS_BOX3D(item)) {
2587         // FIXME: Also deal with multiple selected boxes
2588         SPBox3D *box = SP_BOX3D(item);
2589         Persp3D *persp = box3d_get_perspective(box);
2590         persp_repr = SP_OBJECT_REPR(persp);
2591         if (persp_repr) {
2592             g_object_set_data(tbl, "repr", persp_repr);
2593             Inkscape::GC::anchor(persp_repr);
2594             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2595             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2596         }
2598         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2599         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2601         box3d_resync_toolbar(persp_repr, tbl);
2602     }
2605 static void
2606 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2608     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2609     SPDocument *document = sp_desktop_document(desktop);
2611     // quit if run by the attr_changed listener
2612     // note: it used to work without the differently called freeze_ attributes (here and in
2613     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2614     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2615         return;
2616     }
2618     // in turn, prevent listener from responding
2619     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2621     //Persp3D *persp = document->current_persp3d;
2622     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2623     if (sel_persps.empty()) {
2624         // this can happen when the document is created; we silently ignore it
2625         return;
2626     }
2627     Persp3D *persp = sel_persps.front();
2629     persp->tmat.set_infinite_direction (axis, adj->value);
2630     SP_OBJECT(persp)->updateRepr();
2632     // TODO: use the correct axis here, too
2633     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2635     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2639 static void
2640 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2642     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2645 static void
2646 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2648     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2651 static void
2652 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2654     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2658 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2660     // TODO: Take all selected perspectives into account
2661     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2662     if (sel_persps.empty()) {
2663         // this can happen when the document is created; we silently ignore it
2664         return;
2665     }
2666     Persp3D *persp = sel_persps.front();
2668     bool set_infinite = gtk_toggle_action_get_active(act);
2669     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2672 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2674     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2677 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2679     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2682 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2684     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2687 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2689     EgeAdjustmentAction* eact = 0;
2690     SPDocument *document = sp_desktop_document (desktop);
2691     Persp3D *persp = document->current_persp3d;
2693     EgeAdjustmentAction* box3d_angle_x = 0;
2694     EgeAdjustmentAction* box3d_angle_y = 0;
2695     EgeAdjustmentAction* box3d_angle_z = 0;
2697     /* Angle X */
2698     {
2699         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2700         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2701         eact = create_adjustment_action( "3DBoxAngleXAction",
2702                                          _("Angle in X direction"), _("Angle X:"),
2703                                          // Translators: PL is short for 'perspective line'
2704                                          _("Angle of PLs in X direction"),
2705                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2706                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2707                                          -360.0, 360.0, 1.0, 10.0,
2708                                          labels, values, G_N_ELEMENTS(labels),
2709                                          box3d_angle_x_value_changed );
2710         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2711         g_object_set_data( holder, "box3d_angle_x_action", eact );
2712         box3d_angle_x = eact;
2713     }
2715     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2716         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2717     } else {
2718         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2719     }
2722     /* VP X state */
2723     {
2724         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2725                                                       // Translators: VP is short for 'vanishing point'
2726                                                       _("State of VP in X direction"),
2727                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2728                                                       "toggle_vp_x",
2729                                                       Inkscape::ICON_SIZE_DECORATION );
2730         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2731         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2732         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2733         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2734         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2735     }
2737     /* Angle Y */
2738     {
2739         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2740         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2741         eact = create_adjustment_action( "3DBoxAngleYAction",
2742                                          _("Angle in Y direction"), _("Angle Y:"),
2743                                          // Translators: PL is short for 'perspective line'
2744                                          _("Angle of PLs in Y direction"),
2745                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2746                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2747                                          -360.0, 360.0, 1.0, 10.0,
2748                                          labels, values, G_N_ELEMENTS(labels),
2749                                          box3d_angle_y_value_changed );
2750         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2751         g_object_set_data( holder, "box3d_angle_y_action", eact );
2752         box3d_angle_y = eact;
2753     }
2755     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2756         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2757     } else {
2758         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2759     }
2761     /* VP Y state */
2762     {
2763         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2764                                                       // Translators: VP is short for 'vanishing point'
2765                                                       _("State of VP in Y direction"),
2766                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2767                                                       "toggle_vp_y",
2768                                                       Inkscape::ICON_SIZE_DECORATION );
2769         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2770         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2771         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2772         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2773         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2774     }
2776     /* Angle Z */
2777     {
2778         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2779         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2780         eact = create_adjustment_action( "3DBoxAngleZAction",
2781                                          _("Angle in Z direction"), _("Angle Z:"),
2782                                          // Translators: PL is short for 'perspective line'
2783                                          _("Angle of PLs in Z direction"),
2784                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2785                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2786                                          -360.0, 360.0, 1.0, 10.0,
2787                                          labels, values, G_N_ELEMENTS(labels),
2788                                          box3d_angle_z_value_changed );
2789         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2790         g_object_set_data( holder, "box3d_angle_z_action", eact );
2791         box3d_angle_z = eact;
2792     }
2794     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2795         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2796     } else {
2797         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2798     }
2800     /* VP Z state */
2801     {
2802         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2803                                                       // Translators: VP is short for 'vanishing point'
2804                                                       _("State of VP in Z direction"),
2805                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2806                                                       "toggle_vp_z",
2807                                                       Inkscape::ICON_SIZE_DECORATION );
2808         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2809         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2810         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2811         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2812         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2813     }
2815     sigc::connection *connection = new sigc::connection(
2816         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2817        );
2818     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2819     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2822 //########################
2823 //##       Spiral       ##
2824 //########################
2826 static void
2827 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2829     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2831     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2832         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2833     }
2835     // quit if run by the attr_changed listener
2836     if (g_object_get_data( tbl, "freeze" )) {
2837         return;
2838     }
2840     // in turn, prevent listener from responding
2841     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2843     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2845     bool modmade = false;
2846     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2847          items != NULL;
2848          items = items->next)
2849     {
2850         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2851             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2852             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2853             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2854             modmade = true;
2855         }
2856     }
2858     g_free(namespaced_name);
2860     if (modmade) {
2861         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2862                                    _("Change spiral"));
2863     }
2865     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2868 static void
2869 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2871     sp_spl_tb_value_changed(adj, tbl, "revolution");
2874 static void
2875 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2877     sp_spl_tb_value_changed(adj, tbl, "expansion");
2880 static void
2881 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2883     sp_spl_tb_value_changed(adj, tbl, "t0");
2886 static void
2887 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2889     GtkWidget *tbl = GTK_WIDGET(obj);
2891     GtkAdjustment *adj;
2893     // fixme: make settable
2894     gdouble rev = 5;
2895     gdouble exp = 1.0;
2896     gdouble t0 = 0.0;
2898     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2899     gtk_adjustment_set_value(adj, rev);
2900     gtk_adjustment_value_changed(adj);
2902     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2903     gtk_adjustment_set_value(adj, exp);
2904     gtk_adjustment_value_changed(adj);
2906     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2907     gtk_adjustment_set_value(adj, t0);
2908     gtk_adjustment_value_changed(adj);
2910     spinbutton_defocus(GTK_OBJECT(tbl));
2914 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2915                                          gchar const */*old_value*/, gchar const */*new_value*/,
2916                                          bool /*is_interactive*/, gpointer data)
2918     GtkWidget *tbl = GTK_WIDGET(data);
2920     // quit if run by the _changed callbacks
2921     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2922         return;
2923     }
2925     // in turn, prevent callbacks from responding
2926     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2928     GtkAdjustment *adj;
2929     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2930     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2932     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2933     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2935     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2936     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2938     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2942 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2943     NULL, /* child_added */
2944     NULL, /* child_removed */
2945     spiral_tb_event_attr_changed,
2946     NULL, /* content_changed */
2947     NULL  /* order_changed */
2948 };
2950 static void
2951 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2953     int n_selected = 0;
2954     Inkscape::XML::Node *repr = NULL;
2956     purge_repr_listener( tbl, tbl );
2958     for (GSList const *items = selection->itemList();
2959          items != NULL;
2960          items = items->next)
2961     {
2962         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2963             n_selected++;
2964             repr = SP_OBJECT_REPR((SPItem *) items->data);
2965         }
2966     }
2968     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2970     if (n_selected == 0) {
2971         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2972     } else if (n_selected == 1) {
2973         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2975         if (repr) {
2976             g_object_set_data( tbl, "repr", repr );
2977             Inkscape::GC::anchor(repr);
2978             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2979             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2980         }
2981     } else {
2982         // FIXME: implement averaging of all parameters for multiple selected
2983         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2984         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2985     }
2989 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2991     EgeAdjustmentAction* eact = 0;
2993     {
2994         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2995         ege_output_action_set_use_markup( act, TRUE );
2996         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2997         g_object_set_data( holder, "mode_action", act );
2998     }
3000     /* Revolution */
3001     {
3002         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3003         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3004         eact = create_adjustment_action( "SpiralRevolutionAction",
3005                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3006                                          "tools.shapes.spiral", "revolution", 3.0,
3007                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3008                                          0.01, 1024.0, 0.1, 1.0,
3009                                          labels, values, G_N_ELEMENTS(labels),
3010                                          sp_spl_tb_revolution_value_changed, 1, 2);
3011         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3012     }
3014     /* Expansion */
3015     {
3016         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3017         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3018         eact = create_adjustment_action( "SpiralExpansionAction",
3019                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3020                                          "tools.shapes.spiral", "expansion", 1.0,
3021                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3022                                          0.0, 1000.0, 0.01, 1.0,
3023                                          labels, values, G_N_ELEMENTS(labels),
3024                                          sp_spl_tb_expansion_value_changed);
3025         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3026     }
3028     /* T0 */
3029     {
3030         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3031         gdouble values[] = {0, 0.5, 0.9};
3032         eact = create_adjustment_action( "SpiralT0Action",
3033                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3034                                          "tools.shapes.spiral", "t0", 0.0,
3035                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3036                                          0.0, 0.999, 0.01, 1.0,
3037                                          labels, values, G_N_ELEMENTS(labels),
3038                                          sp_spl_tb_t0_value_changed);
3039         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3040     }
3042     /* Reset */
3043     {
3044         InkAction* inky = ink_action_new( "SpiralResetAction",
3045                                           _("Defaults"),
3046                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3047                                           GTK_STOCK_CLEAR,
3048                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3049         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3050         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3051     }
3054     sigc::connection *connection = new sigc::connection(
3055         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3056         );
3057     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3058     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3061 //########################
3062 //##     Pen/Pencil    ##
3063 //########################
3066 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3068     // Put stuff here
3071 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3073     // Put stuff here
3076 //########################
3077 //##       Tweak        ##
3078 //########################
3080 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3082     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3085 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3087     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3090 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3092     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3095 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3097     int mode = ege_select_one_action_get_active( act );
3098     prefs_set_int_attribute("tools.tweak", "mode", mode);
3100     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3101     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3102     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3103     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3104     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3105     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3106     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3107         if (doh) gtk_action_set_sensitive (doh, TRUE);
3108         if (dos) gtk_action_set_sensitive (dos, TRUE);
3109         if (dol) gtk_action_set_sensitive (dol, TRUE);
3110         if (doo) gtk_action_set_sensitive (doo, TRUE);
3111         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3112         if (fid) gtk_action_set_sensitive (fid, FALSE);
3113     } else {
3114         if (doh) gtk_action_set_sensitive (doh, FALSE);
3115         if (dos) gtk_action_set_sensitive (dos, FALSE);
3116         if (dol) gtk_action_set_sensitive (dol, FALSE);
3117         if (doo) gtk_action_set_sensitive (doo, FALSE);
3118         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3119         if (fid) gtk_action_set_sensitive (fid, TRUE);
3120     }
3123 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3125     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3128 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3129     bool show = gtk_toggle_action_get_active( act );
3130     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3132 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3133     bool show = gtk_toggle_action_get_active( act );
3134     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3136 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3137     bool show = gtk_toggle_action_get_active( act );
3138     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3140 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3141     bool show = gtk_toggle_action_get_active( act );
3142     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3145 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3147     {
3148         /* Width */
3149         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3150         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3151         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3152                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3153                                                               "tools.tweak", "width", 15,
3154                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3155                                                               1, 100, 1.0, 10.0,
3156                                                               labels, values, G_N_ELEMENTS(labels),
3157                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3158         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3159         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3160     }
3163     {
3164         /* Force */
3165         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3166         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3167         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3168                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3169                                                               "tools.tweak", "force", 20,
3170                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3171                                                               1, 100, 1.0, 10.0,
3172                                                               labels, values, G_N_ELEMENTS(labels),
3173                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3174         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3175         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3176     }
3178     /* Mode */
3179     {
3180         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3182         GtkTreeIter iter;
3183         gtk_list_store_append( model, &iter );
3184         gtk_list_store_set( model, &iter,
3185                             0, _("Push mode"),
3186                             1, _("Push parts of paths in any direction"),
3187                             2, "tweak_push_mode",
3188                             -1 );
3190         gtk_list_store_append( model, &iter );
3191         gtk_list_store_set( model, &iter,
3192                             0, _("Shrink mode"),
3193                             1, _("Shrink (inset) parts of paths"),
3194                             2, "tweak_shrink_mode",
3195                             -1 );
3197         gtk_list_store_append( model, &iter );
3198         gtk_list_store_set( model, &iter,
3199                             0, _("Grow mode"),
3200                             1, _("Grow (outset) parts of paths"),
3201                             2, "tweak_grow_mode",
3202                             -1 );
3204         gtk_list_store_append( model, &iter );
3205         gtk_list_store_set( model, &iter,
3206                             0, _("Attract mode"),
3207                             1, _("Attract parts of paths towards cursor"),
3208                             2, "tweak_attract_mode",
3209                             -1 );
3211         gtk_list_store_append( model, &iter );
3212         gtk_list_store_set( model, &iter,
3213                             0, _("Repel mode"),
3214                             1, _("Repel parts of paths from cursor"),
3215                             2, "tweak_repel_mode",
3216                             -1 );
3218         gtk_list_store_append( model, &iter );
3219         gtk_list_store_set( model, &iter,
3220                             0, _("Roughen mode"),
3221                             1, _("Roughen parts of paths"),
3222                             2, "tweak_roughen_mode",
3223                             -1 );
3225         gtk_list_store_append( model, &iter );
3226         gtk_list_store_set( model, &iter,
3227                             0, _("Color paint mode"),
3228                             1, _("Paint the tool's color upon selected objects"),
3229                             2, "tweak_colorpaint_mode",
3230                             -1 );
3232         gtk_list_store_append( model, &iter );
3233         gtk_list_store_set( model, &iter,
3234                             0, _("Color jitter mode"),
3235                             1, _("Jitter the colors of selected objects"),
3236                             2, "tweak_colorjitter_mode",
3237                             -1 );
3239         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3240         g_object_set( act, "short_label", _("Mode:"), NULL );
3241         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3242         g_object_set_data( holder, "mode_action", act );
3244         ege_select_one_action_set_appearance( act, "full" );
3245         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3246         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3247         ege_select_one_action_set_icon_column( act, 2 );
3248         ege_select_one_action_set_tooltip_column( act, 1  );
3250         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3251         ege_select_one_action_set_active( act, mode );
3252         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3254         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3255     }
3257     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3259     {
3260         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3261         ege_output_action_set_use_markup( act, TRUE );
3262         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3263         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3264             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3265         g_object_set_data( holder, "tweak_channels_label", act);
3266     }
3268     {
3269         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3270                                                       _("Hue"),
3271                                                       _("In color mode, act on objects' hue"),
3272                                                       NULL,
3273                                                       Inkscape::ICON_SIZE_DECORATION );
3274         g_object_set( act, "short_label", _("H"), NULL );
3275         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3276         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3277         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3278         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3279             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3280         g_object_set_data( holder, "tweak_doh", act);
3281     }
3282     {
3283         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3284                                                       _("Saturation"),
3285                                                       _("In color mode, act on objects' saturation"),
3286                                                       NULL,
3287                                                       Inkscape::ICON_SIZE_DECORATION );
3288         g_object_set( act, "short_label", _("S"), NULL );
3289         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3290         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3291         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3292         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3293             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3294         g_object_set_data( holder, "tweak_dos", act );
3295     }
3296     {
3297         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3298                                                       _("Lightness"),
3299                                                       _("In color mode, act on objects' lightness"),
3300                                                       NULL,
3301                                                       Inkscape::ICON_SIZE_DECORATION );
3302         g_object_set( act, "short_label", _("L"), NULL );
3303         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3304         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3305         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3306         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3307             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3308         g_object_set_data( holder, "tweak_dol", act );
3309     }
3310     {
3311         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3312                                                       _("Opacity"),
3313                                                       _("In color mode, act on objects' opacity"),
3314                                                       NULL,
3315                                                       Inkscape::ICON_SIZE_DECORATION );
3316         g_object_set( act, "short_label", _("O"), NULL );
3317         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3318         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3319         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3320         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3321             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3322         g_object_set_data( holder, "tweak_doo", act );
3323     }
3325     {   /* Fidelity */
3326         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3327         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3328         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3329                                                               _("Fidelity"), _("Fidelity:"),
3330                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3331                                                               "tools.tweak", "fidelity", 50,
3332                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3333                                                               1, 100, 1.0, 10.0,
3334                                                               labels, values, G_N_ELEMENTS(labels),
3335                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3336         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3337         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3338         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3339             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3340         g_object_set_data( holder, "tweak_fidelity", eact );
3341     }
3344     /* Use Pressure button */
3345     {
3346         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3347                                                       _("Pressure"),
3348                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3349                                                       "use_pressure",
3350                                                       Inkscape::ICON_SIZE_DECORATION );
3351         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3352         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3353         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3354     }
3359 //########################
3360 //##     Calligraphy    ##
3361 //########################
3362 static void update_presets_list(GObject *dataKludge ){
3363     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3364     if (sel) {
3365         ege_select_one_action_set_active(sel, 0 );
3366     }
3369 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3371     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3372     update_presets_list(tbl);
3375 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3377     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3378     update_presets_list(tbl);
3381 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3383     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3384     update_presets_list(tbl);
3387 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3389     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3390     update_presets_list(tbl);
3393 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3395     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3396     update_presets_list(tbl);
3399 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3401     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3402     update_presets_list(tbl);
3405 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3407     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3408     update_presets_list(tbl);
3411 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3413     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3414     update_presets_list(tbl);
3417 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3419     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3420     update_presets_list(tbl);
3423 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3425     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3426     update_presets_list(tbl);
3429 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3431     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3432     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3433     update_presets_list(tbl);
3434     if (calligraphy_angle )
3435         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3439 #define PROFILE_FLOAT_SIZE 7
3440 #define PROFILE_INT_SIZE 4
3441 struct ProfileFloatElement {
3442     char const *name;
3443     double def;
3444     double min;
3445     double max;
3446 };
3447 struct ProfileIntElement {
3448     char const *name;
3449     int def;
3450     int min;
3451     int max;
3452 };
3456 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3457     {"mass",0.02, 0.0, 1.0},
3458     {"wiggle",0.0, 0.0, 1.0},
3459     {"angle",30.0, -90.0, 90.0},
3460     {"thinning",0.1, -1.0, 1.0},
3461     {"tremor",0.0, 0.0, 1.0},
3462     {"flatness",0.9, 0.0, 1.0},
3463     {"cap_rounding",0.0, 0.0, 5.0}
3464 };
3465 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3466     {"width",15, 1, 100},
3467     {"usepressure",1,0,1},
3468     {"tracebackground",0,0,1},
3469     {"usetilt",1,0,1},
3470 };
3474 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3475     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3476     if (! desktop) return;
3478     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3479     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3480     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3482     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3483     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3484     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3485     
3486     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3487         ProfileFloatElement const &pe = f_profile[i];
3488         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3489         prefs_set_double_attribute(pref_path,pe.name,v);
3490     }    
3491     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3492         ProfileIntElement const &pe = i_profile[i];
3493         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3494         prefs_set_int_attribute(pref_path,pe.name,v);
3495     }
3496     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3498     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3499     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3500     GtkTreeIter iter;
3501     gtk_list_store_append( model, &iter );
3502     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3504     free(profile_id);
3505     free(pref_path);
3507     ege_select_one_action_set_active(selector, new_index);
3511 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3512     
3513     gint preset_index = ege_select_one_action_get_active( act ); 
3514     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3515        
3516     if ( profile_name) {        
3517         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3518         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3519             ProfileFloatElement const &pe = f_profile[i];
3520             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3521             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3522             if ( adj ) {
3523                 gtk_adjustment_set_value(adj, v);
3524             }
3525         } 
3526         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3527             ProfileIntElement const &pe = i_profile[i];
3528             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3529             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3530             if ( toggle ) {
3531                 gtk_toggle_action_set_active(toggle, v);
3532             } else printf("No toggle");
3533         }
3534         free(profile_name);
3535         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3536     }
3541 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3543     {
3544         EgeAdjustmentAction* calligraphy_angle = 0;
3546         {
3547         /* Width */
3548         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3549         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3550         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3551                                                               _("Pen Width"), _("Width:"),
3552                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3553                                                               "tools.calligraphic", "width", 15,
3554                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3555                                                               1, 100, 1.0, 10.0,
3556                                                               labels, values, G_N_ELEMENTS(labels),
3557                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3558         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3559         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3560         }
3562         {
3563         /* Thinning */
3564             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3565             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3566         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3567                                                               _("Stroke Thinning"), _("Thinning:"),
3568                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3569                                                               "tools.calligraphic", "thinning", 0.1,
3570                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3571                                                               -1.0, 1.0, 0.01, 0.1,
3572                                                               labels, values, G_N_ELEMENTS(labels),
3573                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3574         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3575         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3576         }
3578         {
3579         /* Angle */
3580         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3581         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3582         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3583                                                               _("Pen Angle"), _("Angle:"),
3584                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3585                                                               "tools.calligraphic", "angle", 30,
3586                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3587                                                               -90.0, 90.0, 1.0, 10.0,
3588                                                               labels, values, G_N_ELEMENTS(labels),
3589                                                               sp_ddc_angle_value_changed, 1, 0 );
3590         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3591         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3592         g_object_set_data( holder, "angle", eact );
3593         calligraphy_angle = eact;
3594         }
3596         {
3597         /* Fixation */
3598             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3599         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3600         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3601                                                               _("Fixation"), _("Fixation:"),
3602                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3603                                                               "tools.calligraphic", "flatness", 0.9,
3604                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3605                                                               0.0, 1.0, 0.01, 0.1,
3606                                                               labels, values, G_N_ELEMENTS(labels),
3607                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3608         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3609         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3610         }
3612         {
3613         /* Cap Rounding */
3614             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3615         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3616         // TRANSLATORS: "cap" means "end" (both start and finish) here
3617         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3618                                                               _("Cap rounding"), _("Caps:"),
3619                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3620                                                               "tools.calligraphic", "cap_rounding", 0.0,
3621                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3622                                                               0.0, 5.0, 0.01, 0.1,
3623                                                               labels, values, G_N_ELEMENTS(labels),
3624                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3625         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3626         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3627         }
3629         {
3630         /* Tremor */
3631             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3632         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3633         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3634                                                               _("Stroke Tremor"), _("Tremor:"),
3635                                                               _("Increase to make strokes rugged and trembling"),
3636                                                               "tools.calligraphic", "tremor", 0.0,
3637                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3638                                                               0.0, 1.0, 0.01, 0.1,
3639                                                               labels, values, G_N_ELEMENTS(labels),
3640                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3642         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3643         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3644         }
3646         {
3647         /* Wiggle */
3648         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3649         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3650         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3651                                                               _("Pen Wiggle"), _("Wiggle:"),
3652                                                               _("Increase to make the pen waver and wiggle"),
3653                                                               "tools.calligraphic", "wiggle", 0.0,
3654                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3655                                                               0.0, 1.0, 0.01, 0.1,
3656                                                               labels, values, G_N_ELEMENTS(labels),
3657                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3658         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3659         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3660         }
3662         {
3663         /* Mass */
3664             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3665         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3666         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3667                                                               _("Pen Mass"), _("Mass:"),
3668                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3669                                                               "tools.calligraphic", "mass", 0.02,
3670                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3671                                                               0.0, 1.0, 0.01, 0.1,
3672                                                               labels, values, G_N_ELEMENTS(labels),
3673                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3674         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3675         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3676         }
3679         /* Trace Background button */
3680         {
3681             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3682                                                           _("Trace Background"),
3683                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3684                                                           "trace_background",
3685                                                           Inkscape::ICON_SIZE_DECORATION );
3686             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3687             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3688             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3689             g_object_set_data( holder, "tracebackground", act );
3690         }
3692         /* Use Pressure button */
3693         {
3694             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3695                                                           _("Pressure"),
3696                                                           _("Use the pressure of the input device to alter the width of the pen"),
3697                                                           "use_pressure",
3698                                                           Inkscape::ICON_SIZE_DECORATION );
3699             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3700             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3701             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3702             g_object_set_data( holder, "usepressure", act );
3703         }
3705         /* Use Tilt button */
3706         {
3707             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3708                                                           _("Tilt"),
3709                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3710                                                           "use_tilt",
3711                                                           Inkscape::ICON_SIZE_DECORATION );
3712             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3713             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3714             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3715             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3716             g_object_set_data( holder, "usetilt", act );
3717         }
3719         /*calligraphic profile */
3720         {
3721             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3722             gchar *pref_path;
3725             GtkTreeIter iter;
3726             gtk_list_store_append( model, &iter );
3727             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3729             //TODO: switch back to prefs API
3730             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3731             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3732             int ii=1;
3733             while (child_repr) {
3734                 GtkTreeIter iter;
3735                 char *preset_name = (char *) child_repr->attribute("name");
3736                 gtk_list_store_append( model, &iter );                
3737                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
3738                 child_repr = sp_repr_next(child_repr); 
3739             }
3741             pref_path = NULL;
3742             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3743             ege_select_one_action_set_appearance( act1, "compact" );
3744             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3745             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3746             g_object_set_data( holder, "profile_selector", act1 );
3747         
3748         }
3750         /*Save or delete calligraphic profile */
3751         {
3752             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3753                                              _("Defaults"),
3754                                              _("Save current settings as new profile"),
3755                                              GTK_STOCK_SAVE );
3756             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3759             gtk_action_group_add_action( mainActions, act );
3760             gtk_action_set_sensitive( act, TRUE );
3761             g_object_set_data( holder, "profile_save_delete", act );
3762         }
3763     }    
3767 //########################
3768 //##    Circle / Arc    ##
3769 //########################
3771 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3773     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3774     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3776     if (v1 == 0 && v2 == 0) {
3777         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3778             gtk_action_set_sensitive( ocb, FALSE );
3779             gtk_action_set_sensitive( make_whole, FALSE );
3780         }
3781     } else {
3782         gtk_action_set_sensitive( ocb, TRUE );
3783         gtk_action_set_sensitive( make_whole, TRUE );
3784     }
3787 static void
3788 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3790     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3792     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3793         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3794     }
3796     // quit if run by the attr_changed listener
3797     if (g_object_get_data( tbl, "freeze" )) {
3798         return;
3799     }
3801     // in turn, prevent listener from responding
3802     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3804     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3806     bool modmade = false;
3807     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3808          items != NULL;
3809          items = items->next)
3810     {
3811         SPItem *item = SP_ITEM(items->data);
3813         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3815             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3816             SPArc *arc = SP_ARC(item);
3818             if (!strcmp(value_name, "start"))
3819                 ge->start = (adj->value * M_PI)/ 180;
3820             else
3821                 ge->end = (adj->value * M_PI)/ 180;
3823             sp_genericellipse_normalize(ge);
3824             ((SPObject *)arc)->updateRepr();
3825             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3827             modmade = true;
3828         }
3829     }
3831     g_free(namespaced_name);
3833     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3835     sp_arctb_sensitivize( tbl, adj->value, other->value );
3837     if (modmade) {
3838         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3839                                    _("Arc: Change start/end"));
3840     }
3842     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3846 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3848     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3851 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3853     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3856 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3858     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3859     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3860         if ( ege_select_one_action_get_active( act ) != 0 ) {
3861             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3862         } else {
3863             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3864         }
3865     }
3867     // quit if run by the attr_changed listener
3868     if (g_object_get_data( tbl, "freeze" )) {
3869         return;
3870     }
3872     // in turn, prevent listener from responding
3873     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3875     bool modmade = false;
3877     if ( ege_select_one_action_get_active(act) != 0 ) {
3878         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3879              items != NULL;
3880              items = items->next)
3881         {
3882             if (SP_IS_ARC((SPItem *) items->data)) {
3883                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3884                 repr->setAttribute("sodipodi:open", "true");
3885                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3886                 modmade = true;
3887             }
3888         }
3889     } else {
3890         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3891              items != NULL;
3892              items = items->next)
3893         {
3894             if (SP_IS_ARC((SPItem *) items->data))    {
3895                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3896                 repr->setAttribute("sodipodi:open", NULL);
3897                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3898                 modmade = true;
3899             }
3900         }
3901     }
3903     if (modmade) {
3904         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3905                                    _("Arc: Change open/closed"));
3906     }
3908     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3911 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3913     GtkAdjustment *adj;
3914     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3915     gtk_adjustment_set_value(adj, 0.0);
3916     gtk_adjustment_value_changed(adj);
3918     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3919     gtk_adjustment_set_value(adj, 0.0);
3920     gtk_adjustment_value_changed(adj);
3922     spinbutton_defocus( GTK_OBJECT(obj) );
3925 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3926                                       gchar const */*old_value*/, gchar const */*new_value*/,
3927                                       bool /*is_interactive*/, gpointer data)
3929     GObject *tbl = G_OBJECT(data);
3931     // quit if run by the _changed callbacks
3932     if (g_object_get_data( tbl, "freeze" )) {
3933         return;
3934     }
3936     // in turn, prevent callbacks from responding
3937     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3939     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3940     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3942     GtkAdjustment *adj1,*adj2;
3943     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3944     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3945     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3946     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3948     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3950     char const *openstr = NULL;
3951     openstr = repr->attribute("sodipodi:open");
3952     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3954     if (openstr) {
3955         ege_select_one_action_set_active( ocb, 1 );
3956     } else {
3957         ege_select_one_action_set_active( ocb, 0 );
3958     }
3960     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3963 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3964     NULL, /* child_added */
3965     NULL, /* child_removed */
3966     arc_tb_event_attr_changed,
3967     NULL, /* content_changed */
3968     NULL  /* order_changed */
3969 };
3972 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3974     int n_selected = 0;
3975     Inkscape::XML::Node *repr = NULL;
3977     purge_repr_listener( tbl, tbl );
3979     for (GSList const *items = selection->itemList();
3980          items != NULL;
3981          items = items->next)
3982     {
3983         if (SP_IS_ARC((SPItem *) items->data)) {
3984             n_selected++;
3985             repr = SP_OBJECT_REPR((SPItem *) items->data);
3986         }
3987     }
3989     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3991     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3992     if (n_selected == 0) {
3993         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3994     } else if (n_selected == 1) {
3995         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3996         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3998         if (repr) {
3999             g_object_set_data( tbl, "repr", repr );
4000             Inkscape::GC::anchor(repr);
4001             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4002             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4003         }
4004     } else {
4005         // FIXME: implement averaging of all parameters for multiple selected
4006         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4007         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4008         sp_arctb_sensitivize( tbl, 1, 0 );
4009     }
4013 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4015     EgeAdjustmentAction* eact = 0;
4018     {
4019         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4020         ege_output_action_set_use_markup( act, TRUE );
4021         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4022         g_object_set_data( holder, "mode_action", act );
4023     }
4025     /* Start */
4026     {
4027         eact = create_adjustment_action( "ArcStartAction",
4028                                          _("Start"), _("Start:"),
4029                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4030                                          "tools.shapes.arc", "start", 0.0,
4031                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4032                                          -360.0, 360.0, 1.0, 10.0,
4033                                          0, 0, 0,
4034                                          sp_arctb_start_value_changed);
4035         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4036     }
4038     /* End */
4039     {
4040         eact = create_adjustment_action( "ArcEndAction",
4041                                          _("End"), _("End:"),
4042                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4043                                          "tools.shapes.arc", "end", 0.0,
4044                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4045                                          -360.0, 360.0, 1.0, 10.0,
4046                                          0, 0, 0,
4047                                          sp_arctb_end_value_changed);
4048         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4049     }
4051     /* Segments / Pie checkbox */
4052     {
4053         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4055         GtkTreeIter iter;
4056         gtk_list_store_append( model, &iter );
4057         gtk_list_store_set( model, &iter,
4058                             0, _("Closed arc"),
4059                             1, _("Switch to segment (closed shape with two radii)"),
4060                             2, "circle_closed_arc",
4061                             -1 );
4063         gtk_list_store_append( model, &iter );
4064         gtk_list_store_set( model, &iter,
4065                             0, _("Open Arc"),
4066                             1, _("Switch to arc (unclosed shape)"),
4067                             2, "circle_open_arc",
4068                             -1 );
4070         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4071         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4072         g_object_set_data( holder, "open_action", act );
4074         ege_select_one_action_set_appearance( act, "full" );
4075         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4076         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4077         ege_select_one_action_set_icon_column( act, 2 );
4078         ege_select_one_action_set_tooltip_column( act, 1  );
4080         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4081         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4082         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4083         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4084     }
4086     /* Make Whole */
4087     {
4088         InkAction* inky = ink_action_new( "ArcResetAction",
4089                                           _("Make whole"),
4090                                           _("Make the shape a whole ellipse, not arc or segment"),
4091                                           "reset_circle",
4092                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4093         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4094         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4095         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4096         g_object_set_data( holder, "make_whole", inky );
4097     }
4099     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4100     // sensitivize make whole and open checkbox
4101     {
4102         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4103         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4104         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4105     }
4108     sigc::connection *connection = new sigc::connection(
4109         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4110         );
4111     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4112     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4118 // toggle button callbacks and updaters
4120 //########################
4121 //##      Dropper       ##
4122 //########################
4124 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4125     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4126     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4127     if ( set_action ) {
4128         if ( gtk_toggle_action_get_active( act ) ) {
4129             gtk_action_set_sensitive( set_action, TRUE );
4130         } else {
4131             gtk_action_set_sensitive( set_action, FALSE );
4132         }
4133     }
4135     spinbutton_defocus(GTK_OBJECT(tbl));
4138 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4139     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4140     spinbutton_defocus(GTK_OBJECT(tbl));
4144 /**
4145  * Dropper auxiliary toolbar construction and setup.
4146  *
4147  * TODO: Would like to add swatch of current color.
4148  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4149  *       can drag and drop places. Will provide a nice mixing palette.
4150  */
4151 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4153     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4155     {
4156         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4157         ege_output_action_set_use_markup( act, TRUE );
4158         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4159     }
4161     {
4162         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4163                                                       _("Pick opacity"),
4164                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4165                                                       NULL,
4166                                                       Inkscape::ICON_SIZE_DECORATION );
4167         g_object_set( act, "short_label", _("Pick"), NULL );
4168         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4169         g_object_set_data( holder, "pick_action", act );
4170         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4171         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4172     }
4174     {
4175         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4176                                                       _("Assign opacity"),
4177                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4178                                                       NULL,
4179                                                       Inkscape::ICON_SIZE_DECORATION );
4180         g_object_set( act, "short_label", _("Assign"), NULL );
4181         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4182         g_object_set_data( holder, "set_action", act );
4183         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4184         // make sure it's disabled if we're not picking alpha
4185         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4186         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4187     }
4191 //########################
4192 //##    Text Toolbox    ##
4193 //########################
4194 /*
4195 static void
4196 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4198     //Call back for letter sizing spinbutton
4201 static void
4202 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4204     //Call back for line height spinbutton
4207 static void
4208 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4210     //Call back for horizontal kerning spinbutton
4213 static void
4214 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4216     //Call back for vertical kerning spinbutton
4219 static void
4220 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4222     //Call back for letter rotation spinbutton
4223 }*/
4225 namespace {
4227 bool popdown_visible = false;
4228 bool popdown_hasfocus = false;
4230 void
4231 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4233     SPStyle *query =
4234         sp_style_new (SP_ACTIVE_DOCUMENT);
4236     int result_fontspec =
4237         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4239     int result_family =
4240         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4242     int result_style =
4243         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4245     int result_numbers =
4246         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4248     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4250     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4251     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4252     {
4253         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4255         if (repr)
4256         {
4257             sp_style_read_from_repr (query, repr);
4258         }
4259         else
4260         {
4261             return;
4262         }
4263     }
4265     if (query->text)
4266     {
4267         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4268             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4269             gtk_entry_set_text (GTK_ENTRY (entry), "");
4271         } else if (query->text->font_specification.value || query->text->font_family.value) {
4273             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4275             // Get the font that corresponds
4276             Glib::ustring familyName;
4278             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4279             if (font) {
4280                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4281                 font->Unref();
4282                 font = NULL;
4283             }
4285             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4287             Gtk::TreePath path;
4288             try {
4289                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4290             } catch (...) {
4291                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4292                 return;
4293             }
4295             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4296             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4298             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4300             gtk_tree_selection_select_path (tselection, path.gobj());
4301             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4303             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4304         }
4306         //Size
4307         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4308         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4309         g_object_set_data (tbl, "size-block", gpointer(1));
4310         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4311         g_object_set_data (tbl, "size-block", gpointer(0));
4312         free (str);
4314         //Anchor
4315         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4316         {
4317             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4318             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4319             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4320             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4321         }
4322         else
4323         {
4324             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4325             {
4326                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4327                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4328                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4329                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4330             }
4331             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4332             {
4333                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4334                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4335                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4336                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4337             }
4338             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4339             {
4340                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4341                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4342                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4343                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4344             }
4345         }
4347         //Style
4348         {
4349             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4351             gboolean active = gtk_toggle_button_get_active (button);
4352             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4354             if (active != check)
4355             {
4356                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4357                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4358                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4359             }
4360         }
4362         {
4363             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4365             gboolean active = gtk_toggle_button_get_active (button);
4366             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4368             if (active != check)
4369             {
4370                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4371                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4372                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4373             }
4374         }
4376         //Orientation
4377         //locking both buttons, changing one affect all group (both)
4378         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4379         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4381         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4382         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4384         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4385         {
4386             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4387         }
4388         else
4389         {
4390             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4391         }
4392         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4393         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4394     }
4396     sp_style_unref(query);
4399 void
4400 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4402     sp_text_toolbox_selection_changed (selection, tbl);
4405 void
4406 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4408     sp_text_toolbox_selection_changed (NULL, tbl);
4411 void
4412 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4413                                 GObject             *tbl)
4415     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4416     GtkTreeModel *model = 0;
4417     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4418     GtkTreeIter   iter;
4419     char         *family = 0;
4421     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4422     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4424     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4425         return;
4426     }
4428     gtk_tree_model_get (model, &iter, 0, &family, -1);
4430     if (g_object_get_data (G_OBJECT (selection), "block"))
4431     {
4432         gtk_entry_set_text (GTK_ENTRY (entry), family);
4433         return;
4434     }
4436     gtk_entry_set_text (GTK_ENTRY (entry), family);
4438     SPStyle *query =
4439         sp_style_new (SP_ACTIVE_DOCUMENT);
4441     int result_fontspec =
4442         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4444     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4446     SPCSSAttr *css = sp_repr_css_attr_new ();
4449     // First try to get the font spec from the stored value
4450     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4452     if (fontSpec.empty()) {
4453         // Construct a new font specification if it does not yet exist
4454         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4455         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4456         fontFromStyle->Unref();
4457     }
4459     if (!fontSpec.empty()) {
4460         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4461         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4462             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4463             if (font) {
4464                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4466                 // Set all the these just in case they were altered when finding the best
4467                 // match for the new family and old style...
4469                 gchar c[256];
4471                 font->Family(c, 256);
4472                 sp_repr_css_set_property (css, "font-family", c);
4474                 font->Attribute( "weight", c, 256);
4475                 sp_repr_css_set_property (css, "font-weight", c);
4477                 font->Attribute("style", c, 256);
4478                 sp_repr_css_set_property (css, "font-style", c);
4480                 font->Attribute("stretch", c, 256);
4481                 sp_repr_css_set_property (css, "font-stretch", c);
4483                 font->Attribute("variant", c, 256);
4484                 sp_repr_css_set_property (css, "font-variant", c);
4486                 font->Unref();
4487             }
4488         }
4489     }
4491     // If querying returned nothing, set the default style of the tool (for new texts)
4492     if (result_fontspec == QUERY_STYLE_NOTHING)
4493     {
4494         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4495         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4496     }
4497     else
4498     {
4499         sp_desktop_set_style (desktop, css, true, true);
4500     }
4502     sp_style_unref(query);
4504     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4505                                    _("Text: Change font family"));
4506     sp_repr_css_attr_unref (css);
4507     free (family);
4508     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4510     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4513 void
4514 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4515                                        GObject      *tbl)
4517     const char *family = gtk_entry_get_text (entry);
4519     try {
4520         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4521         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4522         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4523         gtk_tree_selection_select_path (selection, path.gobj());
4524         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4525         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4526     } catch (...) {
4527         if (family && strlen (family))
4528         {
4529             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4530         }
4531     }
4534 void
4535 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4536                                    gpointer          data)
4538     if (g_object_get_data (G_OBJECT (button), "block")) return;
4539     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4540     int prop = GPOINTER_TO_INT(data);
4542     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4543     SPCSSAttr *css = sp_repr_css_attr_new ();
4545     switch (prop)
4546     {
4547         case 0:
4548         {
4549             sp_repr_css_set_property (css, "text-anchor", "start");
4550             sp_repr_css_set_property (css, "text-align", "start");
4551             break;
4552         }
4553         case 1:
4554         {
4555             sp_repr_css_set_property (css, "text-anchor", "middle");
4556             sp_repr_css_set_property (css, "text-align", "center");
4557             break;
4558         }
4560         case 2:
4561         {
4562             sp_repr_css_set_property (css, "text-anchor", "end");
4563             sp_repr_css_set_property (css, "text-align", "end");
4564             break;
4565         }
4567         case 3:
4568         {
4569             sp_repr_css_set_property (css, "text-anchor", "start");
4570             sp_repr_css_set_property (css, "text-align", "justify");
4571             break;
4572         }
4573     }
4575     SPStyle *query =
4576         sp_style_new (SP_ACTIVE_DOCUMENT);
4577     int result_numbers =
4578         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4580     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4581     if (result_numbers == QUERY_STYLE_NOTHING)
4582     {
4583         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4584     }
4586     sp_style_unref(query);
4588     sp_desktop_set_style (desktop, css, true, true);
4589     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4590                                    _("Text: Change alignment"));
4591     sp_repr_css_attr_unref (css);
4593     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4596 void
4597 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4598                                gpointer          data)
4600     if (g_object_get_data (G_OBJECT (button), "block")) return;
4602     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4603     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4604     int          prop       = GPOINTER_TO_INT(data);
4605     bool         active     = gtk_toggle_button_get_active (button);
4607     SPStyle *query =
4608         sp_style_new (SP_ACTIVE_DOCUMENT);
4610     int result_fontspec =
4611         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4613     int result_family =
4614         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4616     int result_style =
4617         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4619     int result_numbers =
4620         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4622     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4623     Glib::ustring newFontSpec = "";
4625     if (fontSpec.empty()) {
4626         // Construct a new font specification if it does not yet exist
4627         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4628         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4629         fontFromStyle->Unref();
4630     }
4632     switch (prop)
4633     {
4634         case 0:
4635         {
4636             if (!fontSpec.empty()) {
4637                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4638             }
4639             if (fontSpec != newFontSpec) {
4640                 // Don't even set the bold if the font didn't exist on the system
4641                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4642             }
4643             break;
4644         }
4646         case 1:
4647         {
4648             if (!fontSpec.empty()) {
4649                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4650             }
4651             if (fontSpec != newFontSpec) {
4652                 // Don't even set the italic if the font didn't exist on the system
4653                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4654             }
4655             break;
4656         }
4657     }
4659     if (!newFontSpec.empty()) {
4660         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4661     }
4663     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4664     if (result_fontspec == QUERY_STYLE_NOTHING)
4665     {
4666         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4667     }
4669     sp_style_unref(query);
4671     sp_desktop_set_style (desktop, css, true, true);
4672     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4673                                    _("Text: Change font style"));
4674     sp_repr_css_attr_unref (css);
4676     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4679 void
4680 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4681                                      gpointer         data)
4683     if (g_object_get_data (G_OBJECT (button), "block")) {
4684         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4685         return;
4686     }
4688     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4689     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4690     int          prop       = GPOINTER_TO_INT(data);
4692     switch (prop)
4693     {
4694         case 0:
4695         {
4696             sp_repr_css_set_property (css, "writing-mode", "lr");
4697             break;
4698         }
4700         case 1:
4701         {
4702             sp_repr_css_set_property (css, "writing-mode", "tb");
4703             break;
4704         }
4705     }
4707     SPStyle *query =
4708         sp_style_new (SP_ACTIVE_DOCUMENT);
4709     int result_numbers =
4710         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4712     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4713     if (result_numbers == QUERY_STYLE_NOTHING)
4714     {
4715         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4716     }
4718     sp_desktop_set_style (desktop, css, true, true);
4719     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4720                                    _("Text: Change orientation"));
4721     sp_repr_css_attr_unref (css);
4723     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4726 gboolean
4727 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4729     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4730     if (!desktop) return FALSE;
4732     switch (get_group0_keyval (event)) {
4733         case GDK_Escape: // defocus
4734             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4735             sp_text_toolbox_selection_changed (NULL, tbl); // update
4736             return TRUE; // I consumed the event
4737             break;
4738     }
4739     return FALSE;
4742 gboolean
4743 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4745     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4746     if (!desktop) return FALSE;
4748     switch (get_group0_keyval (event)) {
4749         case GDK_KP_Enter:
4750         case GDK_Return:
4751         case GDK_Escape: // defocus
4752             gtk_widget_hide (w);
4753             popdown_visible = false;
4754             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4755             return TRUE; // I consumed the event
4756             break;
4757         case GDK_w:
4758         case GDK_W:
4759             if (event->state & GDK_CONTROL_MASK) {
4760                 gtk_widget_hide (w);
4761                 popdown_visible = false;
4762                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4763                 return TRUE; // I consumed the event
4764             }
4765             break;
4766     }
4767     return FALSE;
4771 void
4772 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4773                                GObject     *tbl)
4775     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4777     if (g_object_get_data (tbl, "size-block")) return;
4779     // If this is not from selecting a size in the list (in which case get_active will give the
4780     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4781     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4782     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4783     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4784         return;
4786     gchar *endptr;
4787     gdouble value = -1;
4788     char *text = gtk_combo_box_get_active_text (cbox);
4789     if (text) {
4790         value = g_strtod (text, &endptr);
4791         if (endptr == text) // conversion failed, non-numeric input
4792             value = -1;
4793         free (text);
4794     }
4795     if (value <= 0) {
4796         return; // could not parse value
4797     }
4799     SPCSSAttr *css = sp_repr_css_attr_new ();
4800     Inkscape::CSSOStringStream osfs;
4801     osfs << value;
4802     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4804     SPStyle *query =
4805         sp_style_new (SP_ACTIVE_DOCUMENT);
4806     int result_numbers =
4807         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4809     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4810     if (result_numbers == QUERY_STYLE_NOTHING)
4811     {
4812         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4813     }
4815     sp_style_unref(query);
4817     sp_desktop_set_style (desktop, css, true, true);
4818     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4819                                    _("Text: Change font size"));
4820     sp_repr_css_attr_unref (css);
4822     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4825 gboolean
4826 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4828     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4829     if (!desktop) return FALSE;
4831     if (!g_object_get_data (tbl, "esc-pressed")) {
4832         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4833         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4834         sp_text_toolbox_size_changed (cbox, tbl);
4835         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4836     }
4837     return FALSE; // I consumed the event
4841 gboolean
4842 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4844     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4845     if (!desktop) return FALSE;
4847     switch (get_group0_keyval (event)) {
4848         case GDK_Escape: // defocus
4849             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4850             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4851             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4852             return TRUE; // I consumed the event
4853             break;
4854         case GDK_Return: // defocus
4855         case GDK_KP_Enter:
4856             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4857             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4858             sp_text_toolbox_size_changed (cbox, tbl);
4859             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4860             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4861             return TRUE; // I consumed the event
4862             break;
4863     }
4864     return FALSE;
4867 void
4868 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4869                                          GObject            *tbl)
4871     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4872     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4873     int x, y;
4875     if (!popdown_visible)
4876     {
4877         gdk_window_get_origin (widget->window, &x, &y);
4878         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4879         gtk_widget_show_all (popdown);
4880         //sp_transientize (popdown);
4882         gdk_pointer_grab (widget->window, TRUE,
4883                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4884                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4885                                         GDK_POINTER_MOTION_MASK),
4886                           NULL, NULL, GDK_CURRENT_TIME);
4888         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4890         popdown_visible = true;
4891     }
4892     else
4893     {
4894         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4895         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4896         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4897         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4898         gtk_widget_hide (popdown);
4899         popdown_visible = false;
4900     }
4903 gboolean
4904 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4905                                  GdkEventFocus    */*event*/,
4906                                  GObject          */*tbl*/)
4908     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4909     return FALSE;
4912 gboolean
4913 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4914                                    GdkEventFocus    */*event*/,
4915                                    GObject          */*tbl*/)
4917     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4919     if (popdown_hasfocus) {
4920         gtk_widget_hide (popdown);
4921         popdown_hasfocus = false;
4922         popdown_visible = false;
4923         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4924         return TRUE;
4925     }
4926     return FALSE;
4929 gboolean
4930 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
4931                                    GdkEventFocus    */*event*/,
4932                                    GObject          */*tbl*/)
4934     popdown_hasfocus = true;
4935     return TRUE;
4939 void
4940 cell_data_func  (GtkTreeViewColumn */*column*/,
4941                  GtkCellRenderer   *cell,
4942                  GtkTreeModel      *tree_model,
4943                  GtkTreeIter       *iter,
4944                  gpointer           /*data*/)
4946     char        *family,
4947         *family_escaped,
4948         *sample_escaped;
4950     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4952     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4954     family_escaped = g_markup_escape_text (family, -1);
4955     sample_escaped = g_markup_escape_text (sample, -1);
4957     std::stringstream markup;
4958     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4959     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4961     free (family);
4962     free (family_escaped);
4963     free (sample_escaped);
4966 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4967     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4968     if (completion) {
4969         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4970         g_object_unref (completion);
4971     }
4974 GtkWidget*
4975 sp_text_toolbox_new (SPDesktop *desktop)
4977     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4979     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4980     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4982     GtkTooltips *tt = gtk_tooltips_new();
4983     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4985     ////////////Family
4986     //Window
4987     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4988     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4990     //Entry
4991     GtkWidget           *entry = gtk_entry_new ();
4992     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4993     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4994     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4995     gtk_entry_completion_set_text_column (completion, 0);
4996     gtk_entry_completion_set_minimum_key_length (completion, 1);
4997     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4998     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4999     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5000     aux_toolbox_space (tbl, 1);
5001     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5002     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5004     //Button
5005     GtkWidget   *button = gtk_button_new ();
5006     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5007     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5009     //Popdown
5010     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5011     GtkWidget           *treeview = gtk_tree_view_new ();
5013     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5014     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5015     gtk_tree_view_column_pack_start (column, cell, FALSE);
5016     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5017     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5018     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5020     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5021     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5022     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5024     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5026     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5027     gtk_container_add (GTK_CONTAINER (sw), treeview);
5029     gtk_container_add (GTK_CONTAINER (window), sw);
5030     gtk_widget_set_size_request (window, 300, 450);
5032     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5033     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5034     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5036     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5038     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5039     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5040     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5042     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5043     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5045     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5046     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5047     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5048     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5049     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5051     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
5052     aux_toolbox_space (tbl, 1);
5053     GtkWidget *box = gtk_event_box_new ();
5054     gtk_container_add (GTK_CONTAINER (box), image);
5055     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5056     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5057     GtkTooltips *tooltips = gtk_tooltips_new ();
5058     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5059     gtk_widget_hide (GTK_WIDGET (box));
5060     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5062     ////////////Size
5063     const char *sizes[] = {
5064         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5065         "16", "18", "20", "22", "24", "28",
5066         "32", "36", "40", "48", "56", "64", "72", "144"
5067     };
5069     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5070     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5071     gtk_widget_set_size_request (cbox, 80, -1);
5072     aux_toolbox_space (tbl, 1);
5073     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5074     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5075     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5076     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5077     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5079     //spacer
5080     aux_toolbox_space (tbl, 4);
5081     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5083     ////////////Text anchor
5084     GtkWidget *group   = gtk_radio_button_new (NULL);
5085     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5086     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5088     // left
5089     GtkWidget *rbutton = group;
5090     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5091     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5092     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5094     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5095     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5096     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5097     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5099     // center
5100     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5101     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5102     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
5103     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5105     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5106     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5107     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5108     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5110     // right
5111     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5112     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5113     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5114     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5116     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5117     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5118     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5119     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5121     // fill
5122     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5123     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5124     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5125     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5127     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5128     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5129     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5130     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5132     aux_toolbox_space (tbl, 1);
5133     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5135     //spacer
5136     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5138     ////////////Text style
5139     row = gtk_hbox_new (FALSE, 4);
5141     // bold
5142     rbutton = gtk_toggle_button_new ();
5143     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5144     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5145     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5146     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5148     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5149     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5150     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5152     // italic
5153     rbutton = gtk_toggle_button_new ();
5154     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5155     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5156     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5157     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5159     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5160     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5161     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5163     aux_toolbox_space (tbl, 1);
5164     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5166     //spacer
5167     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5169     ////////////Text orientation
5170     group   = gtk_radio_button_new (NULL);
5171     row     = gtk_hbox_new (FALSE, 4);
5172     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5174     // horizontal
5175     rbutton = group;
5176     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5177     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5178     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5179     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5181     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5182     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5183     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5185     // vertical
5186     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5187     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5188     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5189     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5190     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5192     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5193     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5194     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5195     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5198     //watch selection
5199     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5201     sigc::connection *c_selection_changed =
5202         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5203                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5204     pool->add_connection ("selection-changed", c_selection_changed);
5206     sigc::connection *c_selection_modified =
5207         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5208                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5209     pool->add_connection ("selection-modified", c_selection_modified);
5211     sigc::connection *c_subselection_changed =
5212         new sigc::connection (desktop->connectToolSubselectionChanged
5213                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5214     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5216     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5219     gtk_widget_show_all (tbl);
5220     return tbl;
5222 } // end of sp_text_toolbox_new()
5224 }//<unnamed> namespace
5227 //#########################
5228 //##      Connector      ##
5229 //#########################
5231 static void sp_connector_path_set_avoid(void)
5233     cc_selection_set_avoid(true);
5237 static void sp_connector_path_set_ignore(void)
5239     cc_selection_set_avoid(false);
5244 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5246     // quit if run by the _changed callbacks
5247     if (g_object_get_data( tbl, "freeze" )) {
5248         return;
5249     }
5251     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5252     SPDocument *doc = sp_desktop_document(desktop);
5254     if (!sp_document_get_undo_sensitive(doc))
5255     {
5256         return;
5257     }
5259     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5261     if ( repr->attribute("inkscape:connector-spacing") ) {
5262         gdouble priorValue = gtk_adjustment_get_value(adj);
5263         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5264         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5265             return;
5266         }
5267     } else if ( adj->value == defaultConnSpacing ) {
5268         return;
5269     }
5271     // in turn, prevent callbacks from responding
5272     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5274     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5275     SP_OBJECT(desktop->namedview)->updateRepr();
5277     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5278     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5279         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5280         NR::Matrix m = NR::identity();
5281         avoid_item_move(&m, item);
5282     }
5284     if (items) {
5285         g_slist_free(items);
5286     }
5288     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5289             _("Change connector spacing"));
5291     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5293     spinbutton_defocus(GTK_OBJECT(tbl));
5296 static void sp_connector_graph_layout(void)
5298     if (!SP_ACTIVE_DESKTOP) return;
5300     // hack for clones, see comment in align-and-distribute.cpp
5301     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5302     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5304     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5306     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5308     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5311 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5313     if ( gtk_toggle_action_get_active( act ) ) {
5314         prefs_set_string_attribute("tools.connector", "directedlayout",
5315                 "true");
5316     } else {
5317         prefs_set_string_attribute("tools.connector", "directedlayout",
5318                 "false");
5319     }
5322 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5324     if ( gtk_toggle_action_get_active( act ) ) {
5325         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5326                 "true");
5327     } else {
5328         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5329                 "false");
5330     }
5334 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5336     prefs_set_double_attribute("tools.connector", "length", adj->value);
5337     spinbutton_defocus(GTK_OBJECT(tbl));
5340 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5341                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5342                                             bool /*is_interactive*/, gpointer data)
5344     GtkWidget *tbl = GTK_WIDGET(data);
5346     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5347         return;
5348     }
5349     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5350         return;
5351     }
5353     GtkAdjustment *adj = (GtkAdjustment*)
5354             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5355     gdouble spacing = defaultConnSpacing;
5356     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5358     gtk_adjustment_set_value(adj, spacing);
5362 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5363     NULL, /* child_added */
5364     NULL, /* child_removed */
5365     connector_tb_event_attr_changed,
5366     NULL, /* content_changed */
5367     NULL  /* order_changed */
5368 };
5371 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5373     {
5374         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5375                                           _("Avoid"),
5376                                           _("Make connectors avoid selected objects"),
5377                                           "connector_avoid",
5378                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5379         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5380         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5381     }
5383     {
5384         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5385                                           _("Ignore"),
5386                                           _("Make connectors ignore selected objects"),
5387                                           "connector_ignore",
5388                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5389         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5390         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5391     }
5393     EgeAdjustmentAction* eact = 0;
5395     // Spacing spinbox
5396     eact = create_adjustment_action( "ConnectorSpacingAction",
5397                                      _("Connector Spacing"), _("Spacing:"),
5398                                      _("The amount of space left around objects by auto-routing connectors"),
5399                                      "tools.connector", "spacing", defaultConnSpacing,
5400                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5401                                      0, 100, 1.0, 10.0,
5402                                      0, 0, 0,
5403                                      connector_spacing_changed, 1, 0 );
5404     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5406     // Graph (connector network) layout
5407     {
5408         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5409                                           _("Graph"),
5410                                           _("Nicely arrange selected connector network"),
5411                                           "graph_layout",
5412                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5413         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5414         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5415     }
5417     // Default connector length spinbox
5418     eact = create_adjustment_action( "ConnectorLengthAction",
5419                                      _("Connector Length"), _("Length:"),
5420                                      _("Ideal length for connectors when layout is applied"),
5421                                      "tools.connector", "length", 100,
5422                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5423                                      10, 1000, 10.0, 100.0,
5424                                      0, 0, 0,
5425                                      connector_length_changed, 1, 0 );
5426     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5429     // Directed edges toggle button
5430     {
5431         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5432                                                       _("Downwards"),
5433                                                       _("Make connectors with end-markers (arrows) point downwards"),
5434                                                       "directed_graph",
5435                                                       Inkscape::ICON_SIZE_DECORATION );
5436         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5438         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5439         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5440                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5442         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5443     }
5445     // Avoid overlaps toggle button
5446     {
5447         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5448                                                       _("Remove overlaps"),
5449                                                       _("Do not allow overlapping shapes"),
5450                                                       "remove_overlaps",
5451                                                       Inkscape::ICON_SIZE_DECORATION );
5452         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5454         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5455         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5456                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5458         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5459     }
5461     // Code to watch for changes to the connector-spacing attribute in
5462     // the XML.
5463     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5464     g_assert(repr != NULL);
5466     purge_repr_listener( holder, holder );
5468     if (repr) {
5469         g_object_set_data( holder, "repr", repr );
5470         Inkscape::GC::anchor(repr);
5471         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5472         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5473     }
5474 } // end of sp_connector_toolbox_prep()
5477 //#########################
5478 //##     Paintbucket     ##
5479 //#########################
5481 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5483     gint channels = ege_select_one_action_get_active( act );
5484     flood_channels_set_channels( channels );
5487 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5489     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5492 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5494     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5497 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5499     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5500     SPUnit const *unit = tracker->getActiveUnit();
5502     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5504     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5507 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5509     // FIXME: make defaults settable via Inkscape Options
5510     struct KeyValue {
5511         char const *key;
5512         double value;
5513     } const key_values[] = {
5514         {"threshold", 15},
5515         {"offset", 0.0}
5516     };
5518     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5519         KeyValue const &kv = key_values[i];
5520         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5521         if ( adj ) {
5522             gtk_adjustment_set_value(adj, kv.value);
5523         }
5524     }
5526     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5527     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5528     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5529     ege_select_one_action_set_active( autogap_action, 0 );
5532 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5534     EgeAdjustmentAction* eact = 0;
5536     {
5537         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5539         GList* items = 0;
5540         gint count = 0;
5541         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5542         {
5543             GtkTreeIter iter;
5544             gtk_list_store_append( model, &iter );
5545             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5546             count++;
5547         }
5548         g_list_free( items );
5549         items = 0;
5550         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5551         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5552         ege_select_one_action_set_appearance( act1, "compact" );
5553         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5554         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5555         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5556         g_object_set_data( holder, "channels_action", act1 );
5557     }
5559     // Spacing spinbox
5560     {
5561         eact = create_adjustment_action(
5562             "ThresholdAction",
5563             _("Fill Threshold"), _("Threshold:"),
5564             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5565             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5566             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5567             0, 0, 0,
5568             paintbucket_threshold_changed, 1, 0 );
5570         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5571     }
5573     // Create the units menu.
5574     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5575     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5576     if (stored_unit)
5577         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5578     g_object_set_data( holder, "tracker", tracker );
5579     {
5580         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5581         gtk_action_group_add_action( mainActions, act );
5582     }
5584     // Offset spinbox
5585     {
5586         eact = create_adjustment_action(
5587             "OffsetAction",
5588             _("Grow/shrink by"), _("Grow/shrink by:"),
5589             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5590             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5591             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5592             0, 0, 0,
5593             paintbucket_offset_changed, 1, 2);
5594         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5596         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5597     }
5599     /* Auto Gap */
5600     {
5601         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5603         GList* items = 0;
5604         gint count = 0;
5605         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5606         {
5607             GtkTreeIter iter;
5608             gtk_list_store_append( model, &iter );
5609             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5610             count++;
5611         }
5612         g_list_free( items );
5613         items = 0;
5614         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5615         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5616         ege_select_one_action_set_appearance( act2, "compact" );
5617         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5618         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5619         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5620         g_object_set_data( holder, "autogap_action", act2 );
5621     }
5623     /* Reset */
5624     {
5625         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5626                                           _("Defaults"),
5627                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5628                                           GTK_STOCK_CLEAR );
5629         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5630         gtk_action_group_add_action( mainActions, act );
5631         gtk_action_set_sensitive( act, TRUE );
5632     }
5636 /*
5637   Local Variables:
5638   mode:c++
5639   c-file-style:"stroustrup"
5640   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5641   indent-tabs-mode:nil
5642   fill-column:99
5643   End:
5644 */
5645 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :