Code

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