Code

More woke on calligraaphic presets:
[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 using Inkscape::UnitTracker;
108 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
109 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
111 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
124 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
130 static struct {
131     gchar const *type_name;
132     gchar const *data_name;
133     sp_verb_t verb;
134     sp_verb_t doubleclick_verb;
135 } const tools[] = {
136     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
137     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
138     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
139     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
140     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
141     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
142     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
143     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
144     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
145     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
146     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
147     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
148     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
149     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
150     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
151     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
152     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
153     { NULL, NULL, 0, 0 }
154 };
156 static struct {
157     gchar const *type_name;
158     gchar const *data_name;
159     GtkWidget *(*create_func)(SPDesktop *desktop);
160     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
161     gchar const *ui_name;
162     gint swatch_verb_id;
163     gchar const *swatch_tool;
164     gchar const *swatch_tip;
165 } const aux_toolboxes[] = {
166     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
167       SP_VERB_INVALID, 0, 0},
168     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
169       SP_VERB_INVALID, 0, 0},
170     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
171       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
172     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
173       SP_VERB_INVALID, 0, 0},
174     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
175       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
176     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
177       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
178     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
179       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
180     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
181       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
182     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
183       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
184     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
185       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
186     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
187       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
188     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
189       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
190     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
191       SP_VERB_INVALID, 0, 0},
192     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
193       SP_VERB_INVALID, 0, 0},
194     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
195       SP_VERB_INVALID, 0, 0},
196     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
197       SP_VERB_INVALID, 0, 0},
198     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
199       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
200     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
201 };
204 static gchar const * ui_descr =
205         "<ui>"
206         "  <toolbar name='SelectToolbar'>"
207         "    <toolitem action='EditSelectAll' />"
208         "    <toolitem action='EditSelectAllInAllLayers' />"
209         "    <toolitem action='EditDeselect' />"
210         "    <separator />"
211         "    <toolitem action='ObjectRotate90CCW' />"
212         "    <toolitem action='ObjectRotate90' />"
213         "    <toolitem action='ObjectFlipHorizontally' />"
214         "    <toolitem action='ObjectFlipVertically' />"
215         "    <separator />"
216         "    <toolitem action='SelectionToBack' />"
217         "    <toolitem action='SelectionLower' />"
218         "    <toolitem action='SelectionRaise' />"
219         "    <toolitem action='SelectionToFront' />"
220         "    <separator />"
221         "    <toolitem action='XAction' />"
222         "    <toolitem action='YAction' />"
223         "    <toolitem action='WidthAction' />"
224         "    <toolitem action='LockAction' />"
225         "    <toolitem action='HeightAction' />"
226         "    <toolitem action='UnitsAction' />"
227         "    <separator />"
228         "    <toolitem action='transform_affect_label' />"
229         "    <toolitem action='transform_stroke' />"
230         "    <toolitem action='transform_corners' />"
231         "    <toolitem action='transform_gradient' />"
232         "    <toolitem action='transform_pattern' />"
233         "  </toolbar>"
235         "  <toolbar name='NodeToolbar'>"
236         "    <toolitem action='NodeInsertAction' />"
237         "    <toolitem action='NodeDeleteAction' />"
238         "    <separator />"
239         "    <toolitem action='NodeJoinAction' />"
240         "    <toolitem action='NodeJoinSegmentAction' />"
241         "    <toolitem action='NodeDeleteSegmentAction' />"
242         "    <toolitem action='NodeBreakAction' />"
243         "    <separator />"
244         "    <toolitem action='NodeCuspAction' />"
245         "    <toolitem action='NodeSmoothAction' />"
246         "    <toolitem action='NodeSymmetricAction' />"
247         "    <separator />"
248         "    <toolitem action='NodeLineAction' />"
249         "    <toolitem action='NodeCurveAction' />"
250         "    <separator />"
251         "    <toolitem action='ObjectToPath' />"
252         "    <toolitem action='StrokeToPath' />"
253         "    <separator />"
254         "    <toolitem action='NodeXAction' />"
255         "    <toolitem action='NodeYAction' />"
256         "    <toolitem action='NodeUnitsAction' />"
257         "    <separator />"
258         "    <toolitem action='ObjectEditClipPathAction' />"
259         "    <toolitem action='ObjectEditMaskPathAction' />"
260         "    <toolitem action='EditNextLPEParameterAction' />"
261         "    <separator />"
262         "    <toolitem action='NodesShowHandlesAction' />"
263         "    <toolitem action='NodesShowHelperpath' />"
264         "  </toolbar>"
266         "  <toolbar name='TweakToolbar'>"
267         "    <toolitem action='TweakWidthAction' />"
268         "    <separator />"
269         "    <toolitem action='TweakForceAction' />"
270         "    <toolitem action='TweakPressureAction' />"
271         "    <separator />"
272         "    <toolitem action='TweakModeAction' />"
273         "    <separator />"
274         "    <toolitem action='TweakFidelityAction' />"
275         "    <separator />"
276         "    <toolitem action='TweakChannelsLabel' />"
277         "    <toolitem action='TweakDoH' />"
278         "    <toolitem action='TweakDoS' />"
279         "    <toolitem action='TweakDoL' />"
280         "    <toolitem action='TweakDoO' />"
281         "  </toolbar>"
283         "  <toolbar name='ZoomToolbar'>"
284         "    <toolitem action='ZoomIn' />"
285         "    <toolitem action='ZoomOut' />"
286         "    <separator />"
287         "    <toolitem action='Zoom1:0' />"
288         "    <toolitem action='Zoom1:2' />"
289         "    <toolitem action='Zoom2:1' />"
290         "    <separator />"
291         "    <toolitem action='ZoomSelection' />"
292         "    <toolitem action='ZoomDrawing' />"
293         "    <toolitem action='ZoomPage' />"
294         "    <toolitem action='ZoomPageWidth' />"
295         "    <separator />"
296         "    <toolitem action='ZoomPrev' />"
297         "    <toolitem action='ZoomNext' />"
298         "  </toolbar>"
300         "  <toolbar name='StarToolbar'>"
301         "    <separator />"
302         "    <toolitem action='StarStateAction' />"
303         "    <separator />"
304         "    <toolitem action='FlatAction' />"
305         "    <separator />"
306         "    <toolitem action='MagnitudeAction' />"
307         "    <toolitem action='SpokeAction' />"
308         "    <toolitem action='RoundednessAction' />"
309         "    <toolitem action='RandomizationAction' />"
310         "    <separator />"
311         "    <toolitem action='StarResetAction' />"
312         "  </toolbar>"
314         "  <toolbar name='RectToolbar'>"
315         "    <toolitem action='RectStateAction' />"
316         "    <toolitem action='RectWidthAction' />"
317         "    <toolitem action='RectHeightAction' />"
318         "    <toolitem action='RadiusXAction' />"
319         "    <toolitem action='RadiusYAction' />"
320         "    <toolitem action='RectUnitsAction' />"
321         "    <separator />"
322         "    <toolitem action='RectResetAction' />"
323         "  </toolbar>"
325         "  <toolbar name='3DBoxToolbar'>"
326         "    <toolitem action='3DBoxAngleXAction' />"
327         "    <toolitem action='3DBoxVPXStateAction' />"
328         "    <separator />"
329         "    <toolitem action='3DBoxAngleYAction' />"
330         "    <toolitem action='3DBoxVPYStateAction' />"
331         "    <separator />"
332         "    <toolitem action='3DBoxAngleZAction' />"
333         "    <toolitem action='3DBoxVPZStateAction' />"
334         "  </toolbar>"
336         "  <toolbar name='SpiralToolbar'>"
337         "    <toolitem action='SpiralStateAction' />"
338         "    <toolitem action='SpiralRevolutionAction' />"
339         "    <toolitem action='SpiralExpansionAction' />"
340         "    <toolitem action='SpiralT0Action' />"
341         "    <separator />"
342         "    <toolitem action='SpiralResetAction' />"
343         "  </toolbar>"
345         "  <toolbar name='PenToolbar'>"
346         "  </toolbar>"
348         "  <toolbar name='PencilToolbar'>"
349         "  </toolbar>"
351         "  <toolbar name='CalligraphyToolbar'>"
352         "    <separator />"
353         "    <toolitem action='SetProfileAction'/>"
354         "    <toolitem action='SaveDeleteProfileAction'/>"
355         "    <separator />"
356         "    <toolitem action='CalligraphyWidthAction' />"
357         "    <toolitem action='PressureAction' />"
358         "    <toolitem action='TraceAction' />"
359         "    <toolitem action='ThinningAction' />"
360         "    <separator />"
361         "    <toolitem action='AngleAction' />"
362         "    <toolitem action='TiltAction' />"
363         "    <toolitem action='FixationAction' />"
364         "    <separator />"
365         "    <toolitem action='CapRoundingAction' />"
366         "    <separator />"
367         "    <toolitem action='TremorAction' />"
368         "    <toolitem action='WiggleAction' />"
369         "    <toolitem action='MassAction' />"
370         "    <separator />"
371         "  </toolbar>"
373         "  <toolbar name='ArcToolbar'>"
374         "    <toolitem action='ArcStateAction' />"
375         "    <separator />"
376         "    <toolitem action='ArcStartAction' />"
377         "    <toolitem action='ArcEndAction' />"
378         "    <separator />"
379         "    <toolitem action='ArcOpenAction' />"
380         "    <separator />"
381         "    <toolitem action='ArcResetAction' />"
382         "    <separator />"
383         "  </toolbar>"
385         "  <toolbar name='PaintbucketToolbar'>"
386         "    <toolitem action='ChannelsAction' />"
387         "    <separator />"
388         "    <toolitem action='ThresholdAction' />"
389         "    <separator />"
390         "    <toolitem action='OffsetAction' />"
391         "    <toolitem action='PaintbucketUnitsAction' />"
392         "    <separator />"
393         "    <toolitem action='AutoGapAction' />"
394         "    <separator />"
395         "    <toolitem action='PaintbucketResetAction' />"
396         "  </toolbar>"
398         "  <toolbar name='DropperToolbar'>"
399         "    <toolitem action='DropperOpacityAction' />"
400         "    <toolitem action='DropperPickAlphaAction' />"
401         "    <toolitem action='DropperSetAlphaAction' />"
402         "  </toolbar>"
404         "  <toolbar name='ConnectorToolbar'>"
405         "    <toolitem action='ConnectorAvoidAction' />"
406         "    <toolitem action='ConnectorIgnoreAction' />"
407         "    <toolitem action='ConnectorSpacingAction' />"
408         "    <toolitem action='ConnectorGraphAction' />"
409         "    <toolitem action='ConnectorLengthAction' />"
410         "    <toolitem action='ConnectorDirectedAction' />"
411         "    <toolitem action='ConnectorOverlapAction' />"
412         "  </toolbar>"
414         "</ui>"
417 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
419 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
421 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
422 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
424 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
425 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
427 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
428 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
430 /* Global text entry widgets necessary for update */
431 /* GtkWidget *dropper_rgb_entry,
432           *dropper_opacity_entry ; */
433 // should be made a private member once this is converted to class
435 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
436     connection->disconnect();
437     delete connection;
440 static void purge_repr_listener( GObject* obj, GObject* tbl )
442     (void)obj;
443     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
444     if (oldrepr) { // remove old listener
445         sp_repr_remove_listener_by_data(oldrepr, tbl);
446         Inkscape::GC::release(oldrepr);
447         oldrepr = 0;
448         g_object_set_data( tbl, "repr", NULL );
449     }
452 GtkWidget *
453 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
454                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
455                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
457     SPAction *action = verb->get_action(view);
458     if (!action) return NULL;
460     SPAction *doubleclick_action;
461     if (doubleclick_verb)
462         doubleclick_action = doubleclick_verb->get_action(view);
463     else
464         doubleclick_action = NULL;
466     /* fixme: Handle sensitive/unsensitive */
467     /* fixme: Implement sp_button_new_from_action */
468     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
469     gtk_widget_show(b);
470     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
472     return b;
475 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
476                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
478     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
481 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
482                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
484     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
488 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
490     SPAction* targetAction = SP_ACTION(user_data);
491     if ( targetAction ) {
492         sp_action_perform( targetAction, NULL );
493     }
496 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
498     if ( data ) {
499         GtkAction* act = GTK_ACTION(data);
500         gtk_action_set_sensitive( act, sensitive );
501     }
504 static SPActionEventVector action_event_vector = {
505     {NULL},
506     NULL,
507     NULL,
508     sp_action_action_set_sensitive,
509     NULL,
510     NULL
511 };
513 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
515     GtkAction* act = 0;
517     SPAction* targetAction = verb->get_action(view);
518     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
519     act = GTK_ACTION(inky);
520     gtk_action_set_sensitive( act, targetAction->sensitive );
522     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
524     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
525     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
527     return act;
530 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
532     Inkscape::UI::View::View *view = desktop;
533     gint verbsToUse[] = {
534         // disabled until we have icons for them:
535         //find
536         //SP_VERB_EDIT_TILE,
537         //SP_VERB_EDIT_UNTILE,
538         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
539         SP_VERB_DIALOG_DISPLAY,
540         SP_VERB_DIALOG_FILL_STROKE,
541         SP_VERB_DIALOG_NAMEDVIEW,
542         SP_VERB_DIALOG_TEXT,
543         SP_VERB_DIALOG_XML_EDITOR,
544         SP_VERB_EDIT_CLONE,
545         SP_VERB_EDIT_COPY,
546         SP_VERB_EDIT_CUT,
547         SP_VERB_EDIT_DUPLICATE,
548         SP_VERB_EDIT_PASTE,
549         SP_VERB_EDIT_REDO,
550         SP_VERB_EDIT_UNDO,
551         SP_VERB_EDIT_UNLINK_CLONE,
552         SP_VERB_FILE_EXPORT,
553         SP_VERB_FILE_IMPORT,
554         SP_VERB_FILE_NEW,
555         SP_VERB_FILE_OPEN,
556         SP_VERB_FILE_PRINT,
557         SP_VERB_FILE_SAVE,
558         SP_VERB_OBJECT_TO_CURVE,
559         SP_VERB_SELECTION_GROUP,
560         SP_VERB_SELECTION_OUTLINE,
561         SP_VERB_SELECTION_UNGROUP,
562         SP_VERB_ZOOM_1_1,
563         SP_VERB_ZOOM_1_2,
564         SP_VERB_ZOOM_2_1,
565         SP_VERB_ZOOM_DRAWING,
566         SP_VERB_ZOOM_IN,
567         SP_VERB_ZOOM_NEXT,
568         SP_VERB_ZOOM_OUT,
569         SP_VERB_ZOOM_PAGE,
570         SP_VERB_ZOOM_PAGE_WIDTH,
571         SP_VERB_ZOOM_PREV,
572         SP_VERB_ZOOM_SELECTION,
573     };
575     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
576     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
578     static std::map<SPDesktop*, GtkActionGroup*> groups;
579     GtkActionGroup* mainActions = 0;
580     if ( groups.find(desktop) != groups.end() ) {
581         mainActions = groups[desktop];
582     }
584     if ( !mainActions ) {
585         mainActions = gtk_action_group_new("main");
586         groups[desktop] = mainActions;
587     }
589     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
590         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
591         if ( verb ) {
592             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
593                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
594                 gtk_action_group_add_action( mainActions, act );
595             }
596         }
597     }
599     return mainActions;
603 GtkWidget *
604 sp_tool_toolbox_new()
606     GtkTooltips *tt = gtk_tooltips_new();
607     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
609     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
610     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
612     gtk_widget_set_sensitive(tb, FALSE);
614     GtkWidget *hb = gtk_handle_box_new();
615     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
616     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
617     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
619     gtk_container_add(GTK_CONTAINER(hb), tb);
620     gtk_widget_show(GTK_WIDGET(tb));
622     sigc::connection* conn = new sigc::connection;
623     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
625     return hb;
628 static void
629 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
631     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
632     gtk_widget_queue_resize(child);
635 static void
636 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
638     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
639     gtk_widget_queue_resize(child);
642 GtkWidget *
643 sp_aux_toolbox_new()
645     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
647     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
649     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
651     gtk_widget_set_sensitive(tb, FALSE);
653     GtkWidget *hb = gtk_handle_box_new();
654     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
655     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
656     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
658     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
659     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
661     gtk_container_add(GTK_CONTAINER(hb), tb);
662     gtk_widget_show(GTK_WIDGET(tb));
664     sigc::connection* conn = new sigc::connection;
665     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
667     return hb;
670 //####################################
671 //# Commands Bar
672 //####################################
674 GtkWidget *
675 sp_commands_toolbox_new()
677     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
679     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
681     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
682     gtk_widget_set_sensitive(tb, FALSE);
684     GtkWidget *hb = gtk_handle_box_new();
685     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
686     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
687     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
689     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
690     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
692     gtk_container_add(GTK_CONTAINER(hb), tb);
693     gtk_widget_show(GTK_WIDGET(tb));
695     sigc::connection* conn = new sigc::connection;
696     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
698     return hb;
701 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
702                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
703                                                        gchar const *path, gchar const *data, gdouble def,
704                                                        GtkWidget *focusTarget,
705                                                        GtkWidget *us,
706                                                        GObject *dataKludge,
707                                                        gboolean altx, gchar const *altx_mark,
708                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
709                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
710                                                        void (*callback)(GtkAdjustment *, GObject *),
711                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
713     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
714                                                              lower, upper, step, page, page ) );
715     if (us) {
716         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
717     }
719     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
721     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
722     if ( shortLabel ) {
723         g_object_set( act, "short_label", shortLabel, NULL );
724     }
726     if ( (descrCount > 0) && descrLabels && descrValues ) {
727         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
728     }
730     if ( focusTarget ) {
731         ege_adjustment_action_set_focuswidget( act, focusTarget );
732     }
734     if ( altx && altx_mark ) {
735         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
736     }
738     if ( dataKludge ) {
739         g_object_set_data( dataKludge, data, adj );
740     }
742     // Using a cast just to make sure we pass in the right kind of function pointer
743     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
745     return act;
749 //####################################
750 //# node editing callbacks
751 //####################################
753 /**
754  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
755  */
756 static ShapeEditor *get_current_shape_editor()
758     if (!SP_ACTIVE_DESKTOP) {
759         return NULL;
760     }
762     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
764     if (!SP_IS_NODE_CONTEXT(event_context)) {
765         return NULL;
766     }
768     return SP_NODE_CONTEXT(event_context)->shape_editor;
772 void
773 sp_node_path_edit_add(void)
775     ShapeEditor *shape_editor = get_current_shape_editor();
776     if (shape_editor) shape_editor->add_node();
779 void
780 sp_node_path_edit_delete(void)
782     ShapeEditor *shape_editor = get_current_shape_editor();
783     if (shape_editor) shape_editor->delete_nodes();
786 void
787 sp_node_path_edit_delete_segment(void)
789     ShapeEditor *shape_editor = get_current_shape_editor();
790     if (shape_editor) shape_editor->delete_segment();
793 void
794 sp_node_path_edit_break(void)
796     ShapeEditor *shape_editor = get_current_shape_editor();
797     if (shape_editor) shape_editor->break_at_nodes();
800 void
801 sp_node_path_edit_join(void)
803     ShapeEditor *shape_editor = get_current_shape_editor();
804     if (shape_editor) shape_editor->join_nodes();
807 void
808 sp_node_path_edit_join_segment(void)
810     ShapeEditor *shape_editor = get_current_shape_editor();
811     if (shape_editor) shape_editor->join_segments();
814 void
815 sp_node_path_edit_toline(void)
817     ShapeEditor *shape_editor = get_current_shape_editor();
818     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
821 void
822 sp_node_path_edit_tocurve(void)
824     ShapeEditor *shape_editor = get_current_shape_editor();
825     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
828 void
829 sp_node_path_edit_cusp(void)
831     ShapeEditor *shape_editor = get_current_shape_editor();
832     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
835 void
836 sp_node_path_edit_smooth(void)
838     ShapeEditor *shape_editor = get_current_shape_editor();
839     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
842 void
843 sp_node_path_edit_symmetrical(void)
845     ShapeEditor *shape_editor = get_current_shape_editor();
846     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
849 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
850     bool show = gtk_toggle_action_get_active( act );
851     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
852     ShapeEditor *shape_editor = get_current_shape_editor();
853     if (shape_editor) shape_editor->show_handles(show);
856 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
857     bool show = gtk_toggle_action_get_active( act );
858     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
859     ShapeEditor *shape_editor = get_current_shape_editor();
860     if (shape_editor) shape_editor->show_helperpath(show);
863 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
864     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
867 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
868     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
871 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
872     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
875 /* is called when the node selection is modified */
876 static void
877 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
879     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
880     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
881     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
882     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
884     // quit if run by the attr_changed listener
885     if (g_object_get_data( tbl, "freeze" )) {
886         return;
887     }
889     // in turn, prevent listener from responding
890     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
892     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
893     SPUnit const *unit = tracker->getActiveUnit();
895     ShapeEditor *shape_editor = get_current_shape_editor();
896     if (shape_editor && shape_editor->has_nodepath()) {
897         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
898         int n_selected = 0;
899         if (nodepath) {
900             n_selected = nodepath->numSelected();
901         }
903         if (n_selected == 0) {
904             gtk_action_set_sensitive(xact, FALSE);
905             gtk_action_set_sensitive(yact, FALSE);
906         } else {
907             gtk_action_set_sensitive(xact, TRUE);
908             gtk_action_set_sensitive(yact, TRUE);
909             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
910             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
912             if (n_selected == 1) {
913                 NR::Point sel_node = nodepath->singleSelectedCoords();
914                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
915                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
916                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
917                 }
918             } else {
919                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
920                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
921                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
922                     /* Note: Currently x and y will always have a value, even if the coordinates of the
923                        selected nodes don't coincide (in this case we use the coordinates of the center
924                        of the bounding box). So the entries are never set to zero. */
925                     // FIXME: Maybe we should clear the entry if several nodes are selected
926                     //        instead of providing a kind of average value
927                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
928                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
929                 }
930             }
931         }
932     } else {
933         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
934         gtk_action_set_sensitive(xact, FALSE);
935         gtk_action_set_sensitive(yact, FALSE);
936     }
938     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
941 static void
942 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
944     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
946     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
947     SPUnit const *unit = tracker->getActiveUnit();
949     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
950         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
951     }
953     // quit if run by the attr_changed listener
954     if (g_object_get_data( tbl, "freeze" )) {
955         return;
956     }
958     // in turn, prevent listener from responding
959     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
961     ShapeEditor *shape_editor = get_current_shape_editor();
962     if (shape_editor && shape_editor->has_nodepath()) {
963         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
964         if (!strcmp(value_name, "x")) {
965             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
966         }
967         if (!strcmp(value_name, "y")) {
968             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
969         }
970     }
972     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
975 static void
976 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
978     sp_node_path_value_changed(adj, tbl, "x");
981 static void
982 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
984     sp_node_path_value_changed(adj, tbl, "y");
987 void
988 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
990     {
991     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
992     SPItem *item = selection->singleItem();
993     if (item && SP_IS_SHAPE(item)) {
994        LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(item));
995        if (lpeobj) {
996            gtk_action_set_sensitive(w, TRUE);
997        } else {
998            gtk_action_set_sensitive(w, FALSE);
999        }
1000     } else {
1001        gtk_action_set_sensitive(w, FALSE);
1002     }
1003     }
1005     {
1006     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1007     SPItem *item = selection->singleItem();
1008     if (item && item->clip_ref && item->clip_ref->getObject()) {
1009        gtk_action_set_sensitive(w, TRUE);
1010     } else {
1011        gtk_action_set_sensitive(w, FALSE);
1012     }
1013     }
1015     {
1016     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1017     SPItem *item = selection->singleItem();
1018     if (item && item->mask_ref && item->mask_ref->getObject()) {
1019        gtk_action_set_sensitive(w, TRUE);
1020     } else {
1021        gtk_action_set_sensitive(w, FALSE);
1022     }
1023     }
1026 void
1027 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1029     sp_node_toolbox_sel_changed (selection, tbl);
1034 //################################
1035 //##    Node Editing Toolbox    ##
1036 //################################
1038 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1040     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1041     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1042     g_object_set_data( holder, "tracker", tracker );
1044     {
1045         InkAction* inky = ink_action_new( "NodeInsertAction",
1046                                           _("Insert node"),
1047                                           _("Insert new nodes into selected segments"),
1048                                           "node_insert",
1049                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1050         g_object_set( inky, "short_label", _("Insert"), NULL );
1051         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1052         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1053     }
1055     {
1056         InkAction* inky = ink_action_new( "NodeDeleteAction",
1057                                           _("Delete node"),
1058                                           _("Delete selected nodes"),
1059                                           "node_delete",
1060                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1061         g_object_set( inky, "short_label", _("Delete"), NULL );
1062         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1063         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1064     }
1066     {
1067         InkAction* inky = ink_action_new( "NodeJoinAction",
1068                                           _("Join endnodes"),
1069                                           _("Join selected endnodes"),
1070                                           "node_join",
1071                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1072         g_object_set( inky, "short_label", _("Join"), NULL );
1073         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1074         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1075     }
1077     {
1078         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1079                                           _("Join Segment"),
1080                                           _("Join selected endnodes with a new segment"),
1081                                           "node_join_segment",
1082                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1083         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1084         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1085     }
1087     {
1088         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1089                                           _("Delete Segment"),
1090                                           _("Split path between two non-endpoint nodes"),
1091                                           "node_delete_segment",
1092                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1093         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1094         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1095     }
1097     {
1098         InkAction* inky = ink_action_new( "NodeBreakAction",
1099                                           _("Node Break"),
1100                                           _("Break path at selected nodes"),
1101                                           "node_break",
1102                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1103         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1104         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1105     }
1107     {
1108         InkAction* inky = ink_action_new( "NodeCuspAction",
1109                                           _("Node Cusp"),
1110                                           _("Make selected nodes corner"),
1111                                           "node_cusp",
1112                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1113         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1114         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1115     }
1117     {
1118         InkAction* inky = ink_action_new( "NodeSmoothAction",
1119                                           _("Node Smooth"),
1120                                           _("Make selected nodes smooth"),
1121                                           "node_smooth",
1122                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1123         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1124         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1125     }
1127     {
1128         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1129                                           _("Node Symmetric"),
1130                                           _("Make selected nodes symmetric"),
1131                                           "node_symmetric",
1132                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1133         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1134         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1135     }
1137     {
1138         InkAction* inky = ink_action_new( "NodeLineAction",
1139                                           _("Node Line"),
1140                                           _("Make selected segments lines"),
1141                                           "node_line",
1142                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1143         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1144         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1145     }
1147     {
1148         InkAction* inky = ink_action_new( "NodeCurveAction",
1149                                           _("Node Curve"),
1150                                           _("Make selected segments curves"),
1151                                           "node_curve",
1152                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1153         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1154         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1155     }
1157     {
1158         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1159                                                       _("Show Handles"),
1160                                                       _("Show the Bezier handles of selected nodes"),
1161                                                       "nodes_show_handles",
1162                                                       Inkscape::ICON_SIZE_DECORATION );
1163         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1164         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1165         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1166     }
1168     {
1169         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1170                                                       _("Show Outline"),
1171                                                       _("Show the outline of the path"),
1172                                                       "nodes_show_helperpath",
1173                                                       Inkscape::ICON_SIZE_DECORATION );
1174         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1175         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1176         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1177     }
1179     {
1180         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1181                                           _("Next path effect parameter"),
1182                                           _("Show next path effect parameter for editing"),
1183                                           "edit_next_parameter",
1184                                           Inkscape::ICON_SIZE_DECORATION );
1185         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1186         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1187         g_object_set_data( holder, "nodes_lpeedit", inky);
1188     }
1190     {
1191         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1192                                           _("Edit clipping path"),
1193                                           _("Edit the clipping path of the object"),
1194                                           "nodeedit-clippath",
1195                                           Inkscape::ICON_SIZE_DECORATION );
1196         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1197         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1198         g_object_set_data( holder, "nodes_clippathedit", inky);
1199     }
1201     {
1202         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1203                                           _("Edit mask path"),
1204                                           _("Edit the mask of the object"),
1205                                           "nodeedit-mask",
1206                                           Inkscape::ICON_SIZE_DECORATION );
1207         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1208         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1209         g_object_set_data( holder, "nodes_maskedit", inky);
1210     }
1212     /* X coord of selected node(s) */
1213     {
1214         EgeAdjustmentAction* eact = 0;
1215         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1216         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1217         eact = create_adjustment_action( "NodeXAction",
1218                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1219                                          "tools.nodes", "Xcoord", 0,
1220                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1221                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1222                                          labels, values, G_N_ELEMENTS(labels),
1223                                          sp_node_path_x_value_changed );
1224         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1225         g_object_set_data( holder, "nodes_x_action", eact );
1226         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1227         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1228     }
1230     /* Y coord of selected node(s) */
1231     {
1232         EgeAdjustmentAction* eact = 0;
1233         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1234         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1235         eact = create_adjustment_action( "NodeYAction",
1236                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1237                                          "tools.nodes", "Ycoord", 0,
1238                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1239                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1240                                          labels, values, G_N_ELEMENTS(labels),
1241                                          sp_node_path_y_value_changed );
1242         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1243         g_object_set_data( holder, "nodes_y_action", eact );
1244         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1245         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1246     }
1248     // add the units menu
1249     {
1250         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1251         gtk_action_group_add_action( mainActions, act );
1252     }
1255     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1257     //watch selection
1258     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1260     sigc::connection *c_selection_changed =
1261         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1262                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1263     pool->add_connection ("selection-changed", c_selection_changed);
1265     sigc::connection *c_selection_modified =
1266         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1267                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1268     pool->add_connection ("selection-modified", c_selection_modified);
1270     sigc::connection *c_subselection_changed =
1271         new sigc::connection (desktop->connectToolSubselectionChanged
1272                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1273     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1275     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1277     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1278 } // end of sp_node_toolbox_prep()
1281 //########################
1282 //##    Zoom Toolbox    ##
1283 //########################
1285 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1287     // no custom GtkAction setup needed
1288 } // end of sp_zoom_toolbox_prep()
1290 void
1291 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1293     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")));
1297 void
1298 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1300     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")));
1303 void
1304 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1306     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")));
1309 static void
1310 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1312     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1313     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1315     if (old_desktop) {
1316         GList *children, *iter;
1318         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1319         for ( iter = children ; iter ; iter = iter->next ) {
1320             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1321         }
1322         g_list_free(children);
1323     }
1325     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1327     if (desktop) {
1328         gtk_widget_set_sensitive(toolbox, TRUE);
1329         setup_func(toolbox, desktop);
1330         update_func(desktop, desktop->event_context, toolbox);
1331         *conn = desktop->connectEventContextChanged
1332             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1333     } else {
1334         gtk_widget_set_sensitive(toolbox, FALSE);
1335     }
1337 } // end of toolbox_set_desktop()
1340 static void
1341 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1343     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1344     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1345     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1346         // "toolbox.tools" was not set. Fallback to older value
1347         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1349         // Copy the setting forwards
1350         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1351     }
1352     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1354     for (int i = 0 ; tools[i].type_name ; i++ ) {
1355         GtkWidget *button =
1356             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1357                                                               SP_BUTTON_TYPE_TOGGLE,
1358                                                               Inkscape::Verb::get(tools[i].verb),
1359                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1360                                                               desktop,
1361                                                               tooltips );
1363         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1364                            (gpointer)button );
1365     }
1369 static void
1370 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1372     gchar const *const tname = ( eventcontext
1373                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1374                                  : NULL );
1375     for (int i = 0 ; tools[i].type_name ; i++ ) {
1376         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1377         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1378     }
1381 static void
1382 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1384     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1385     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1386     GtkUIManager* mgr = gtk_ui_manager_new();
1387     GError* errVal = 0;
1388     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1389     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1391     std::map<std::string, GtkWidget*> dataHolders;
1393     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1394         if ( aux_toolboxes[i].prep_func ) {
1395             // converted to GtkActions and UIManager
1397             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1398             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1399             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1400             dataHolders[aux_toolboxes[i].type_name] = kludge;
1401             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1402         } else {
1404             GtkWidget *sub_toolbox = 0;
1405             if (aux_toolboxes[i].create_func == NULL)
1406                 sub_toolbox = sp_empty_toolbox_new(desktop);
1407             else {
1408                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1409             }
1411             gtk_size_group_add_widget( grouper, sub_toolbox );
1413             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1414             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1416         }
1417     }
1419     // Second pass to create toolbars *after* all GtkActions are created
1420     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1421         if ( aux_toolboxes[i].prep_func ) {
1422             // converted to GtkActions and UIManager
1424             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1426             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1427             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1429             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1430             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1431             g_free( tmp );
1432             tmp = 0;
1434             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1435             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1436             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1437                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1438             }
1439             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1442             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1444             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1445                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1446                 swatch->setDesktop( desktop );
1447                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1448                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1449                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1450                 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 );
1451             }
1453             gtk_widget_show_all( holder );
1454             sp_set_font_size_smaller( holder );
1456             gtk_size_group_add_widget( grouper, holder );
1458             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1459             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1460         }
1461     }
1463     g_object_unref( G_OBJECT(grouper) );
1466 static void
1467 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1469     gchar const *tname = ( eventcontext
1470                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1471                            : NULL );
1472     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1473         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1474         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1475             gtk_widget_show_all(sub_toolbox);
1476             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1477         } else {
1478             gtk_widget_hide(sub_toolbox);
1479         }
1480     }
1483 static void
1484 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1486     gchar const * descr =
1487         "<ui>"
1488         "  <toolbar name='CommandsToolbar'>"
1489         "    <toolitem action='FileNew' />"
1490         "    <toolitem action='FileOpen' />"
1491         "    <toolitem action='FileSave' />"
1492         "    <toolitem action='FilePrint' />"
1493         "    <separator />"
1494         "    <toolitem action='FileImport' />"
1495         "    <toolitem action='FileExport' />"
1496         "    <separator />"
1497         "    <toolitem action='EditUndo' />"
1498         "    <toolitem action='EditRedo' />"
1499         "    <separator />"
1500         "    <toolitem action='EditCopy' />"
1501         "    <toolitem action='EditCut' />"
1502         "    <toolitem action='EditPaste' />"
1503         "    <separator />"
1504         "    <toolitem action='ZoomSelection' />"
1505         "    <toolitem action='ZoomDrawing' />"
1506         "    <toolitem action='ZoomPage' />"
1507         "    <separator />"
1508         "    <toolitem action='EditDuplicate' />"
1509         "    <toolitem action='EditClone' />"
1510         "    <toolitem action='EditUnlinkClone' />"
1511         "    <separator />"
1512         "    <toolitem action='SelectionGroup' />"
1513         "    <toolitem action='SelectionUnGroup' />"
1514         "    <separator />"
1515         "    <toolitem action='DialogFillStroke' />"
1516         "    <toolitem action='DialogText' />"
1517         "    <toolitem action='DialogXMLEditor' />"
1518         "    <toolitem action='DialogAlignDistribute' />"
1519         "    <separator />"
1520         "    <toolitem action='DialogPreferences' />"
1521         "    <toolitem action='DialogDocumentProperties' />"
1522         "  </toolbar>"
1523         "</ui>";
1524     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1527     GtkUIManager* mgr = gtk_ui_manager_new();
1528     GError* errVal = 0;
1530     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1531     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1533     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1534     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1535         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1536     }
1537     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1538     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1539     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1542     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1545 static void
1546 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1550 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1552     gtk_widget_show(toolbox_toplevel);
1553     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1555     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1556     if (!shown_toolbox) {
1557         return;
1558     }
1559     gtk_widget_show(toolbox);
1561     gtk_widget_show_all(shown_toolbox);
1564 void
1565 aux_toolbox_space(GtkWidget *tb, gint space)
1567     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1570 static GtkWidget *
1571 sp_empty_toolbox_new(SPDesktop *desktop)
1573     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1574     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1575     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1577     gtk_widget_show_all(tbl);
1578     sp_set_font_size_smaller (tbl);
1580     return tbl;
1583 // helper UI functions
1585 GtkWidget *
1586 sp_tb_spinbutton(
1587     gchar *label, gchar const *tooltip,
1588     gchar const *path, gchar const *data, gdouble def,
1589     GtkWidget *us,
1590     GtkWidget *tbl,
1591     gboolean altx, gchar const *altx_mark,
1592     gdouble lower, gdouble upper, gdouble step, gdouble page,
1593     void (*callback)(GtkAdjustment *, GtkWidget *),
1594     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1596     GtkTooltips *tt = gtk_tooltips_new();
1598     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1600     GtkWidget *l = gtk_label_new(label);
1601     gtk_widget_show(l);
1602     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1603     gtk_container_add(GTK_CONTAINER(hb), l);
1605     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1606                                       lower, upper, step, page, page);
1607     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1608     if (us)
1609         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1611     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1612     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1613     if (altx)
1614         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1615     gtk_widget_set_size_request(sb,
1616                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1617                                 AUX_SPINBUTTON_HEIGHT);
1618     gtk_widget_show(sb);
1619     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1620     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1621     gtk_container_add(GTK_CONTAINER(hb), sb);
1622     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1624     return hb;
1627 #define MODE_LABEL_WIDTH 70
1629 //########################
1630 //##       Star         ##
1631 //########################
1633 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1635     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1637     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1638         // do not remember prefs if this call is initiated by an undo change, because undoing object
1639         // creation sets bogus values to its attributes before it is deleted
1640         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1641     }
1643     // quit if run by the attr_changed listener
1644     if (g_object_get_data( dataKludge, "freeze" )) {
1645         return;
1646     }
1648     // in turn, prevent listener from responding
1649     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1651     bool modmade = false;
1653     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1654     GSList const *items = selection->itemList();
1655     for (; items != NULL; items = items->next) {
1656         if (SP_IS_STAR((SPItem *) items->data)) {
1657             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1658             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1659             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1660                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1661                                     + M_PI / (gint)adj->value));
1662             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1663             modmade = true;
1664         }
1665     }
1666     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1667                                    _("Star: Change number of corners"));
1669     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1672 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1674     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1676     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1677         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1678     }
1680     // quit if run by the attr_changed listener
1681     if (g_object_get_data( dataKludge, "freeze" )) {
1682         return;
1683     }
1685     // in turn, prevent listener from responding
1686     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1688     bool modmade = false;
1689     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1690     GSList const *items = selection->itemList();
1691     for (; items != NULL; items = items->next) {
1692         if (SP_IS_STAR((SPItem *) items->data)) {
1693             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1695             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1696             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1697             if (r2 < r1) {
1698                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1699             } else {
1700                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1701             }
1703             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1704             modmade = true;
1705         }
1706     }
1708     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1709                                    _("Star: Change spoke ratio"));
1711     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1714 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1716     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1717     bool flat = ege_select_one_action_get_active( act ) == 0;
1719     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1720         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1721                                     flat ? "true" : "false" );
1722     }
1724     // quit if run by the attr_changed listener
1725     if (g_object_get_data( dataKludge, "freeze" )) {
1726         return;
1727     }
1729     // in turn, prevent listener from responding
1730     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1732     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1733     GSList const *items = selection->itemList();
1734     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1735     bool modmade = false;
1737     if ( prop_action ) {
1738         gtk_action_set_sensitive( prop_action, !flat );
1739     }
1741     for (; items != NULL; items = items->next) {
1742         if (SP_IS_STAR((SPItem *) items->data)) {
1743             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1744             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1745             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1746             modmade = true;
1747         }
1748     }
1750     if (modmade) {
1751         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1752                          flat ? _("Make polygon") : _("Make star"));
1753     }
1755     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1758 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1760     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1762     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1763         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1764     }
1766     // quit if run by the attr_changed listener
1767     if (g_object_get_data( dataKludge, "freeze" )) {
1768         return;
1769     }
1771     // in turn, prevent listener from responding
1772     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1774     bool modmade = false;
1776     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1777     GSList const *items = selection->itemList();
1778     for (; items != NULL; items = items->next) {
1779         if (SP_IS_STAR((SPItem *) items->data)) {
1780             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1781             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1782             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1783             modmade = true;
1784         }
1785     }
1786     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1787                                    _("Star: Change rounding"));
1789     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1792 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1794     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1796     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1797         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1798     }
1800     // quit if run by the attr_changed listener
1801     if (g_object_get_data( dataKludge, "freeze" )) {
1802         return;
1803     }
1805     // in turn, prevent listener from responding
1806     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1808     bool modmade = false;
1810     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1811     GSList const *items = selection->itemList();
1812     for (; items != NULL; items = items->next) {
1813         if (SP_IS_STAR((SPItem *) items->data)) {
1814             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1815             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1816             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1817             modmade = true;
1818         }
1819     }
1820     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1821                                    _("Star: Change randomization"));
1823     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1827 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1828                                        gchar const */*old_value*/, gchar const */*new_value*/,
1829                                        bool /*is_interactive*/, gpointer data)
1831     GtkWidget *tbl = GTK_WIDGET(data);
1833     // quit if run by the _changed callbacks
1834     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1835         return;
1836     }
1838     // in turn, prevent callbacks from responding
1839     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1841     GtkAdjustment *adj = 0;
1843     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1844     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1846     if (!strcmp(name, "inkscape:randomized")) {
1847         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1848         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1849     } else if (!strcmp(name, "inkscape:rounded")) {
1850         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1851         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1852     } else if (!strcmp(name, "inkscape:flatsided")) {
1853         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1854         char const *flatsides = repr->attribute("inkscape:flatsided");
1855         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1856         if ( flatsides && !strcmp(flatsides,"false") ) {
1857             ege_select_one_action_set_active( flat_action, 1 );
1858             gtk_action_set_sensitive( prop_action, TRUE );
1859         } else {
1860             ege_select_one_action_set_active( flat_action, 0 );
1861             gtk_action_set_sensitive( prop_action, FALSE );
1862         }
1863     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1864         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1865         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1866         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1867         if (r2 < r1) {
1868             gtk_adjustment_set_value(adj, r2/r1);
1869         } else {
1870             gtk_adjustment_set_value(adj, r1/r2);
1871         }
1872     } else if (!strcmp(name, "sodipodi:sides")) {
1873         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1874         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1875     }
1877     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1881 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1883     NULL, /* child_added */
1884     NULL, /* child_removed */
1885     star_tb_event_attr_changed,
1886     NULL, /* content_changed */
1887     NULL  /* order_changed */
1888 };
1891 /**
1892  *  \param selection Should not be NULL.
1893  */
1894 static void
1895 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1897     int n_selected = 0;
1898     Inkscape::XML::Node *repr = NULL;
1900     purge_repr_listener( tbl, tbl );
1902     for (GSList const *items = selection->itemList();
1903          items != NULL;
1904          items = items->next)
1905     {
1906         if (SP_IS_STAR((SPItem *) items->data)) {
1907             n_selected++;
1908             repr = SP_OBJECT_REPR((SPItem *) items->data);
1909         }
1910     }
1912     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1914     if (n_selected == 0) {
1915         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1916     } else if (n_selected == 1) {
1917         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1919         if (repr) {
1920             g_object_set_data( tbl, "repr", repr );
1921             Inkscape::GC::anchor(repr);
1922             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1923             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1924         }
1925     } else {
1926         // FIXME: implement averaging of all parameters for multiple selected stars
1927         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1928         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1929     }
1933 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1935     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1936     // callbacks to lump all the changes for all selected objects in one undo step
1938     GtkAdjustment *adj = 0;
1940     // fixme: make settable in prefs!
1941     gint mag = 5;
1942     gdouble prop = 0.5;
1943     gboolean flat = FALSE;
1944     gdouble randomized = 0;
1945     gdouble rounded = 0;
1947     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1948     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1950     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1951     gtk_action_set_sensitive( sb2, !flat );
1953     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1954     gtk_adjustment_set_value(adj, mag);
1955     gtk_adjustment_value_changed(adj);
1957     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1958     gtk_adjustment_set_value(adj, prop);
1959     gtk_adjustment_value_changed(adj);
1961     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1962     gtk_adjustment_set_value(adj, rounded);
1963     gtk_adjustment_value_changed(adj);
1965     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1966     gtk_adjustment_set_value(adj, randomized);
1967     gtk_adjustment_value_changed(adj);
1971 void
1972 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1974     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1975     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1976     GtkWidget *l = gtk_label_new(NULL);
1977     gtk_label_set_markup(GTK_LABEL(l), title);
1978     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1979     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1980     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1984 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1986     {
1987         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1988         ege_output_action_set_use_markup( act, TRUE );
1989         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1990         g_object_set_data( holder, "mode_action", act );
1991     }
1993     {
1994         EgeAdjustmentAction* eact = 0;
1995         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1996         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1998         /* Flatsided checkbox */
1999         {
2000             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2002             GtkTreeIter iter;
2003             gtk_list_store_append( model, &iter );
2004             gtk_list_store_set( model, &iter,
2005                                 0, _("Polygon"),
2006                                 1, _("Regular polygon (with one handle) instead of a star"),
2007                                 2, "star_flat",
2008                                 -1 );
2010             gtk_list_store_append( model, &iter );
2011             gtk_list_store_set( model, &iter,
2012                                 0, _("Star"),
2013                                 1, _("Star instead of a regular polygon (with one handle)"),
2014                                 2, "star_angled",
2015                                 -1 );
2017             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2018             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2019             g_object_set_data( holder, "flat_action", act );
2021             ege_select_one_action_set_appearance( act, "full" );
2022             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2023             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2024             ege_select_one_action_set_icon_column( act, 2 );
2025             ege_select_one_action_set_tooltip_column( act, 1  );
2027             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2028             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2029         }
2031         /* Magnitude */
2032         {
2033         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2034         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2035         eact = create_adjustment_action( "MagnitudeAction",
2036                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2037                                          "tools.shapes.star", "magnitude", 3,
2038                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2039                                          3, 1024, 1, 5,
2040                                          labels, values, G_N_ELEMENTS(labels),
2041                                          sp_stb_magnitude_value_changed,
2042                                          1.0, 0 );
2043         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2044         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2045         }
2047         /* Spoke ratio */
2048         {
2049         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2050         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2051         eact = create_adjustment_action( "SpokeAction",
2052                                          _("Spoke ratio"), _("Spoke ratio:"),
2053                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2054                                          // Base radius is the same for the closest handle.
2055                                          _("Base radius to tip radius ratio"),
2056                                          "tools.shapes.star", "proportion", 0.5,
2057                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2058                                          0.01, 1.0, 0.01, 0.1,
2059                                          labels, values, G_N_ELEMENTS(labels),
2060                                          sp_stb_proportion_value_changed );
2061         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2062         g_object_set_data( holder, "prop_action", eact );
2063         }
2065         if ( !isFlatSided ) {
2066             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2067         } else {
2068             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2069         }
2071         /* Roundedness */
2072         {
2073         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2074         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2075         eact = create_adjustment_action( "RoundednessAction",
2076                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2077                                          "tools.shapes.star", "rounded", 0.0,
2078                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2079                                          -10.0, 10.0, 0.01, 0.1,
2080                                          labels, values, G_N_ELEMENTS(labels),
2081                                          sp_stb_rounded_value_changed );
2082         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2083         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2084         }
2086         /* Randomization */
2087         {
2088         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2089         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2090         eact = create_adjustment_action( "RandomizationAction",
2091                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2092                                          "tools.shapes.star", "randomized", 0.0,
2093                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2094                                          -10.0, 10.0, 0.001, 0.01,
2095                                          labels, values, G_N_ELEMENTS(labels),
2096                                          sp_stb_randomized_value_changed, 0.1, 3 );
2097         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2098         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2099         }
2100     }
2102     {
2103         /* Reset */
2104         {
2105             GtkAction* act = gtk_action_new( "StarResetAction",
2106                                              _("Defaults"),
2107                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2108                                              GTK_STOCK_CLEAR );
2109             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2110             gtk_action_group_add_action( mainActions, act );
2111             gtk_action_set_sensitive( act, TRUE );
2112         }
2113     }
2115     sigc::connection *connection = new sigc::connection(
2116         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2117         );
2118     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2119     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2123 //########################
2124 //##       Rect         ##
2125 //########################
2127 static void sp_rtb_sensitivize( GObject *tbl )
2129     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2130     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2131     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2133     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2134         gtk_action_set_sensitive( not_rounded, FALSE );
2135     } else {
2136         gtk_action_set_sensitive( not_rounded, TRUE );
2137     }
2141 static void
2142 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2143                           void (*setter)(SPRect *, gdouble))
2145     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2147     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2148     SPUnit const *unit = tracker->getActiveUnit();
2150     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2151         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2152     }
2154     // quit if run by the attr_changed listener
2155     if (g_object_get_data( tbl, "freeze" )) {
2156         return;
2157     }
2159     // in turn, prevent listener from responding
2160     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2162     bool modmade = false;
2163     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2164     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2165         if (SP_IS_RECT(items->data)) {
2166             if (adj->value != 0) {
2167                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2168             } else {
2169                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2170             }
2171             modmade = true;
2172         }
2173     }
2175     sp_rtb_sensitivize( tbl );
2177     if (modmade) {
2178         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2179                                    _("Change rectangle"));
2180     }
2182     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2185 static void
2186 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2188     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2191 static void
2192 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2194     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2197 static void
2198 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2200     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2203 static void
2204 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2206     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2211 static void
2212 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2214     GtkAdjustment *adj = 0;
2216     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2217     gtk_adjustment_set_value(adj, 0.0);
2218     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2219     gtk_adjustment_value_changed(adj);
2221     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2222     gtk_adjustment_set_value(adj, 0.0);
2223     gtk_adjustment_value_changed(adj);
2225     sp_rtb_sensitivize( obj );
2228 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2229                                        gchar const */*old_value*/, gchar const */*new_value*/,
2230                                        bool /*is_interactive*/, gpointer data)
2232     GObject *tbl = G_OBJECT(data);
2234     // quit if run by the _changed callbacks
2235     if (g_object_get_data( tbl, "freeze" )) {
2236         return;
2237     }
2239     // in turn, prevent callbacks from responding
2240     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2242     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2243     SPUnit const *unit = tracker->getActiveUnit();
2245     gpointer item = g_object_get_data( tbl, "item" );
2246     if (item && SP_IS_RECT(item)) {
2247         {
2248             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2249             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2250             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2251         }
2253         {
2254             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2255             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2256             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2257         }
2259         {
2260             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2261             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2262             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2263         }
2265         {
2266             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2267             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2268             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2269         }
2270     }
2272     sp_rtb_sensitivize( tbl );
2274     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2278 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2279     NULL, /* child_added */
2280     NULL, /* child_removed */
2281     rect_tb_event_attr_changed,
2282     NULL, /* content_changed */
2283     NULL  /* order_changed */
2284 };
2286 /**
2287  *  \param selection should not be NULL.
2288  */
2289 static void
2290 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2292     int n_selected = 0;
2293     Inkscape::XML::Node *repr = NULL;
2294     SPItem *item = NULL;
2296     if ( g_object_get_data( tbl, "repr" ) ) {
2297         g_object_set_data( tbl, "item", NULL );
2298     }
2299     purge_repr_listener( tbl, tbl );
2301     for (GSList const *items = selection->itemList();
2302          items != NULL;
2303          items = items->next) {
2304         if (SP_IS_RECT((SPItem *) items->data)) {
2305             n_selected++;
2306             item = (SPItem *) items->data;
2307             repr = SP_OBJECT_REPR(item);
2308         }
2309     }
2311     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2313     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2315     if (n_selected == 0) {
2316         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2318         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2319         gtk_action_set_sensitive(w, FALSE);
2320         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2321         gtk_action_set_sensitive(h, FALSE);
2323     } else if (n_selected == 1) {
2324         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2325         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2327         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2328         gtk_action_set_sensitive(w, TRUE);
2329         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2330         gtk_action_set_sensitive(h, TRUE);
2332         if (repr) {
2333             g_object_set_data( tbl, "repr", repr );
2334             g_object_set_data( tbl, "item", item );
2335             Inkscape::GC::anchor(repr);
2336             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2337             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2338         }
2339     } else {
2340         // FIXME: implement averaging of all parameters for multiple selected
2341         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2342         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2343         sp_rtb_sensitivize( tbl );
2344     }
2348 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2350     EgeAdjustmentAction* eact = 0;
2352     {
2353         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2354         ege_output_action_set_use_markup( act, TRUE );
2355         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2356         g_object_set_data( holder, "mode_action", act );
2357     }
2359     // rx/ry units menu: create
2360     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2361     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2362     // fixme: add % meaning per cent of the width/height
2363     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2364     g_object_set_data( holder, "tracker", tracker );
2366     /* W */
2367     {
2368         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2369         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2370         eact = create_adjustment_action( "RectWidthAction",
2371                                          _("Width"), _("W:"), _("Width of rectangle"),
2372                                          "tools.shapes.rect", "width", 0,
2373                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2374                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2375                                          labels, values, G_N_ELEMENTS(labels),
2376                                          sp_rtb_width_value_changed );
2377         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2378         g_object_set_data( holder, "width_action", eact );
2379         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2380         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2381     }
2383     /* H */
2384     {
2385         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2386         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2387         eact = create_adjustment_action( "RectHeightAction",
2388                                          _("Height"), _("H:"), _("Height of rectangle"),
2389                                          "tools.shapes.rect", "height", 0,
2390                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2391                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2392                                          labels, values, G_N_ELEMENTS(labels),
2393                                          sp_rtb_height_value_changed );
2394         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2395         g_object_set_data( holder, "height_action", eact );
2396         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2397         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2398     }
2400     /* rx */
2401     {
2402         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2403         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2404         eact = create_adjustment_action( "RadiusXAction",
2405                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2406                                          "tools.shapes.rect", "rx", 0,
2407                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2408                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2409                                          labels, values, G_N_ELEMENTS(labels),
2410                                          sp_rtb_rx_value_changed);
2411         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2412         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2413     }
2415     /* ry */
2416     {
2417         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2418         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2419         eact = create_adjustment_action( "RadiusYAction",
2420                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2421                                          "tools.shapes.rect", "ry", 0,
2422                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2423                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2424                                          labels, values, G_N_ELEMENTS(labels),
2425                                          sp_rtb_ry_value_changed);
2426         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2427         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2428     }
2430     // add the units menu
2431     {
2432         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2433         gtk_action_group_add_action( mainActions, act );
2434     }
2436     /* Reset */
2437     {
2438         InkAction* inky = ink_action_new( "RectResetAction",
2439                                           _("Not rounded"),
2440                                           _("Make corners sharp"),
2441                                           "squared_corner",
2442                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2443         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2444         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2445         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2446         g_object_set_data( holder, "not_rounded", inky );
2447     }
2449     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2450     sp_rtb_sensitivize( holder );
2452     sigc::connection *connection = new sigc::connection(
2453         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2454         );
2455     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2456     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2459 //########################
2460 //##       3D Box       ##
2461 //########################
2463 // normalize angle so that it lies in the interval [0,360]
2464 static double box3d_normalize_angle (double a) {
2465     double angle = a + ((int) (a/360.0))*360;
2466     if (angle < 0) {
2467         angle += 360.0;
2468     }
2469     return angle;
2472 static void
2473 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2474                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2475     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2476     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2477     //       are reset).
2478     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2480     if (is_infinite) {
2481         gtk_toggle_action_set_active(tact, TRUE);
2482         gtk_action_set_sensitive(act, TRUE);
2484         double angle = persp3d_get_infinite_angle(persp, axis);
2485         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2486             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2487         }
2488     } else {
2489         gtk_toggle_action_set_active(tact, FALSE);
2490         gtk_action_set_sensitive(act, FALSE);
2491     }
2494 static void
2495 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2496     if (!persp_repr) {
2497         g_print ("No perspective given to box3d_resync_toolbar().\n");
2498         return;
2499     }
2501     GtkWidget *tbl = GTK_WIDGET(data);
2502     GtkAdjustment *adj = 0;
2503     GtkAction *act = 0;
2504     GtkToggleAction *tact = 0;
2505     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2506     {
2507         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2508         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2509         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2511         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2512     }
2513     {
2514         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2515         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2516         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2518         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2519     }
2520     {
2521         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2522         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2523         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2525         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2526     }
2529 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2530                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2531                                                   bool /*is_interactive*/, gpointer data)
2533     GtkWidget *tbl = GTK_WIDGET(data);
2535     // quit if run by the attr_changed listener
2536     // note: it used to work without the differently called freeze_ attributes (here and in
2537     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2538     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2539         return;
2540     }
2542     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2543     // sp_document_maybe_done() when the document is undo insensitive)
2544     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2546     // TODO: Only update the appropriate part of the toolbar
2547 //    if (!strcmp(name, "inkscape:vp_z")) {
2548         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2549 //    }
2551     Persp3D *persp = persp3d_get_from_repr(repr);
2552     persp3d_update_box_reprs(persp);
2554     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2557 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2559     NULL, /* child_added */
2560     NULL, /* child_removed */
2561     box3d_persp_tb_event_attr_changed,
2562     NULL, /* content_changed */
2563     NULL  /* order_changed */
2564 };
2566 /**
2567  *  \param selection Should not be NULL.
2568  */
2569 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2570 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2571 static void
2572 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2574     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2575     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2576     // update the perspectives with infinite VPs and leave the other ones untouched).
2578     Inkscape::XML::Node *persp_repr = NULL;
2579     purge_repr_listener(tbl, tbl);
2581     SPItem *item = selection->singleItem();
2582     if (item && SP_IS_BOX3D(item)) {
2583         // FIXME: Also deal with multiple selected boxes
2584         SPBox3D *box = SP_BOX3D(item);
2585         Persp3D *persp = box3d_get_perspective(box);
2586         persp_repr = SP_OBJECT_REPR(persp);
2587         if (persp_repr) {
2588             g_object_set_data(tbl, "repr", persp_repr);
2589             Inkscape::GC::anchor(persp_repr);
2590             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2591             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2592         }
2594         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2595         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2597         box3d_resync_toolbar(persp_repr, tbl);
2598     }
2601 static void
2602 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2604     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2605     SPDocument *document = sp_desktop_document(desktop);
2607     // quit if run by the attr_changed listener
2608     // note: it used to work without the differently called freeze_ attributes (here and in
2609     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2610     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2611         return;
2612     }
2614     // in turn, prevent listener from responding
2615     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2617     //Persp3D *persp = document->current_persp3d;
2618     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2619     if (sel_persps.empty()) {
2620         // this can happen when the document is created; we silently ignore it
2621         return;
2622     }
2623     Persp3D *persp = sel_persps.front();
2625     persp->tmat.set_infinite_direction (axis, adj->value);
2626     SP_OBJECT(persp)->updateRepr();
2628     // TODO: use the correct axis here, too
2629     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2631     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2635 static void
2636 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2638     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2641 static void
2642 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2644     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2647 static void
2648 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2650     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2654 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2656     // TODO: Take all selected perspectives into account
2657     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2658     if (sel_persps.empty()) {
2659         // this can happen when the document is created; we silently ignore it
2660         return;
2661     }
2662     Persp3D *persp = sel_persps.front();
2664     bool set_infinite = gtk_toggle_action_get_active(act);
2665     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2668 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2670     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2673 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2675     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2678 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2680     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2683 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2685     EgeAdjustmentAction* eact = 0;
2686     SPDocument *document = sp_desktop_document (desktop);
2687     Persp3D *persp = document->current_persp3d;
2689     EgeAdjustmentAction* box3d_angle_x = 0;
2690     EgeAdjustmentAction* box3d_angle_y = 0;
2691     EgeAdjustmentAction* box3d_angle_z = 0;
2693     /* Angle X */
2694     {
2695         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2696         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2697         eact = create_adjustment_action( "3DBoxAngleXAction",
2698                                          _("Angle in X direction"), _("Angle X:"),
2699                                          // Translators: PL is short for 'perspective line'
2700                                          _("Angle of PLs in X direction"),
2701                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2702                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2703                                          -360.0, 360.0, 1.0, 10.0,
2704                                          labels, values, G_N_ELEMENTS(labels),
2705                                          box3d_angle_x_value_changed );
2706         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2707         g_object_set_data( holder, "box3d_angle_x_action", eact );
2708         box3d_angle_x = eact;
2709     }
2711     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2712         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2713     } else {
2714         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2715     }
2718     /* VP X state */
2719     {
2720         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2721                                                       // Translators: VP is short for 'vanishing point'
2722                                                       _("State of VP in X direction"),
2723                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2724                                                       "toggle_vp_x",
2725                                                       Inkscape::ICON_SIZE_DECORATION );
2726         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2727         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2728         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2729         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2730         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2731     }
2733     /* Angle Y */
2734     {
2735         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2736         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2737         eact = create_adjustment_action( "3DBoxAngleYAction",
2738                                          _("Angle in Y direction"), _("Angle Y:"),
2739                                          // Translators: PL is short for 'perspective line'
2740                                          _("Angle of PLs in Y direction"),
2741                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2742                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2743                                          -360.0, 360.0, 1.0, 10.0,
2744                                          labels, values, G_N_ELEMENTS(labels),
2745                                          box3d_angle_y_value_changed );
2746         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2747         g_object_set_data( holder, "box3d_angle_y_action", eact );
2748         box3d_angle_y = eact;
2749     }
2751     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2752         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2753     } else {
2754         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2755     }
2757     /* VP Y state */
2758     {
2759         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2760                                                       // Translators: VP is short for 'vanishing point'
2761                                                       _("State of VP in Y direction"),
2762                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2763                                                       "toggle_vp_y",
2764                                                       Inkscape::ICON_SIZE_DECORATION );
2765         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2766         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2767         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2768         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2769         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2770     }
2772     /* Angle Z */
2773     {
2774         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2775         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2776         eact = create_adjustment_action( "3DBoxAngleZAction",
2777                                          _("Angle in Z direction"), _("Angle Z:"),
2778                                          // Translators: PL is short for 'perspective line'
2779                                          _("Angle of PLs in Z direction"),
2780                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2781                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2782                                          -360.0, 360.0, 1.0, 10.0,
2783                                          labels, values, G_N_ELEMENTS(labels),
2784                                          box3d_angle_z_value_changed );
2785         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2786         g_object_set_data( holder, "box3d_angle_z_action", eact );
2787         box3d_angle_z = eact;
2788     }
2790     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2791         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2792     } else {
2793         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2794     }
2796     /* VP Z state */
2797     {
2798         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2799                                                       // Translators: VP is short for 'vanishing point'
2800                                                       _("State of VP in Z direction"),
2801                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2802                                                       "toggle_vp_z",
2803                                                       Inkscape::ICON_SIZE_DECORATION );
2804         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2805         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2806         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2807         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2808         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2809     }
2811     sigc::connection *connection = new sigc::connection(
2812         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2813        );
2814     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2815     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2818 //########################
2819 //##       Spiral       ##
2820 //########################
2822 static void
2823 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2825     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2827     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2828         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2829     }
2831     // quit if run by the attr_changed listener
2832     if (g_object_get_data( tbl, "freeze" )) {
2833         return;
2834     }
2836     // in turn, prevent listener from responding
2837     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2839     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2841     bool modmade = false;
2842     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2843          items != NULL;
2844          items = items->next)
2845     {
2846         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2847             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2848             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2849             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2850             modmade = true;
2851         }
2852     }
2854     g_free(namespaced_name);
2856     if (modmade) {
2857         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2858                                    _("Change spiral"));
2859     }
2861     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2864 static void
2865 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2867     sp_spl_tb_value_changed(adj, tbl, "revolution");
2870 static void
2871 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2873     sp_spl_tb_value_changed(adj, tbl, "expansion");
2876 static void
2877 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2879     sp_spl_tb_value_changed(adj, tbl, "t0");
2882 static void
2883 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2885     GtkWidget *tbl = GTK_WIDGET(obj);
2887     GtkAdjustment *adj;
2889     // fixme: make settable
2890     gdouble rev = 5;
2891     gdouble exp = 1.0;
2892     gdouble t0 = 0.0;
2894     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2895     gtk_adjustment_set_value(adj, rev);
2896     gtk_adjustment_value_changed(adj);
2898     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2899     gtk_adjustment_set_value(adj, exp);
2900     gtk_adjustment_value_changed(adj);
2902     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2903     gtk_adjustment_set_value(adj, t0);
2904     gtk_adjustment_value_changed(adj);
2906     spinbutton_defocus(GTK_OBJECT(tbl));
2910 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2911                                          gchar const */*old_value*/, gchar const */*new_value*/,
2912                                          bool /*is_interactive*/, gpointer data)
2914     GtkWidget *tbl = GTK_WIDGET(data);
2916     // quit if run by the _changed callbacks
2917     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2918         return;
2919     }
2921     // in turn, prevent callbacks from responding
2922     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2924     GtkAdjustment *adj;
2925     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2926     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2928     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2929     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2931     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2932     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2934     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2938 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2939     NULL, /* child_added */
2940     NULL, /* child_removed */
2941     spiral_tb_event_attr_changed,
2942     NULL, /* content_changed */
2943     NULL  /* order_changed */
2944 };
2946 static void
2947 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2949     int n_selected = 0;
2950     Inkscape::XML::Node *repr = NULL;
2952     purge_repr_listener( tbl, tbl );
2954     for (GSList const *items = selection->itemList();
2955          items != NULL;
2956          items = items->next)
2957     {
2958         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2959             n_selected++;
2960             repr = SP_OBJECT_REPR((SPItem *) items->data);
2961         }
2962     }
2964     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2966     if (n_selected == 0) {
2967         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2968     } else if (n_selected == 1) {
2969         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2971         if (repr) {
2972             g_object_set_data( tbl, "repr", repr );
2973             Inkscape::GC::anchor(repr);
2974             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2975             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2976         }
2977     } else {
2978         // FIXME: implement averaging of all parameters for multiple selected
2979         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2980         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2981     }
2985 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2987     EgeAdjustmentAction* eact = 0;
2989     {
2990         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2991         ege_output_action_set_use_markup( act, TRUE );
2992         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2993         g_object_set_data( holder, "mode_action", act );
2994     }
2996     /* Revolution */
2997     {
2998         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2999         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3000         eact = create_adjustment_action( "SpiralRevolutionAction",
3001                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3002                                          "tools.shapes.spiral", "revolution", 3.0,
3003                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3004                                          0.01, 1024.0, 0.1, 1.0,
3005                                          labels, values, G_N_ELEMENTS(labels),
3006                                          sp_spl_tb_revolution_value_changed, 1, 2);
3007         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3008     }
3010     /* Expansion */
3011     {
3012         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3013         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3014         eact = create_adjustment_action( "SpiralExpansionAction",
3015                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3016                                          "tools.shapes.spiral", "expansion", 1.0,
3017                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3018                                          0.0, 1000.0, 0.01, 1.0,
3019                                          labels, values, G_N_ELEMENTS(labels),
3020                                          sp_spl_tb_expansion_value_changed);
3021         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3022     }
3024     /* T0 */
3025     {
3026         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3027         gdouble values[] = {0, 0.5, 0.9};
3028         eact = create_adjustment_action( "SpiralT0Action",
3029                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3030                                          "tools.shapes.spiral", "t0", 0.0,
3031                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3032                                          0.0, 0.999, 0.01, 1.0,
3033                                          labels, values, G_N_ELEMENTS(labels),
3034                                          sp_spl_tb_t0_value_changed);
3035         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3036     }
3038     /* Reset */
3039     {
3040         InkAction* inky = ink_action_new( "SpiralResetAction",
3041                                           _("Defaults"),
3042                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3043                                           GTK_STOCK_CLEAR,
3044                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3045         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3046         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3047     }
3050     sigc::connection *connection = new sigc::connection(
3051         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3052         );
3053     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3054     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3057 //########################
3058 //##     Pen/Pencil    ##
3059 //########################
3062 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3064     // Put stuff here
3067 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3069     // Put stuff here
3072 //########################
3073 //##       Tweak        ##
3074 //########################
3076 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3078     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3081 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3083     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3086 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3088     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3091 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3093     int mode = ege_select_one_action_get_active( act );
3094     prefs_set_int_attribute("tools.tweak", "mode", mode);
3096     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3097     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3098     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3099     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3100     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3101     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3102     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3103         if (doh) gtk_action_set_sensitive (doh, TRUE);
3104         if (dos) gtk_action_set_sensitive (dos, TRUE);
3105         if (dol) gtk_action_set_sensitive (dol, TRUE);
3106         if (doo) gtk_action_set_sensitive (doo, TRUE);
3107         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3108         if (fid) gtk_action_set_sensitive (fid, FALSE);
3109     } else {
3110         if (doh) gtk_action_set_sensitive (doh, FALSE);
3111         if (dos) gtk_action_set_sensitive (dos, FALSE);
3112         if (dol) gtk_action_set_sensitive (dol, FALSE);
3113         if (doo) gtk_action_set_sensitive (doo, FALSE);
3114         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3115         if (fid) gtk_action_set_sensitive (fid, TRUE);
3116     }
3119 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3121     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3124 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3125     bool show = gtk_toggle_action_get_active( act );
3126     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3128 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3129     bool show = gtk_toggle_action_get_active( act );
3130     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3132 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3133     bool show = gtk_toggle_action_get_active( act );
3134     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3136 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3137     bool show = gtk_toggle_action_get_active( act );
3138     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3141 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3143     {
3144         /* Width */
3145         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3146         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3147         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3148                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3149                                                               "tools.tweak", "width", 15,
3150                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3151                                                               1, 100, 1.0, 10.0,
3152                                                               labels, values, G_N_ELEMENTS(labels),
3153                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3154         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3155         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3156     }
3159     {
3160         /* Force */
3161         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3162         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3163         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3164                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3165                                                               "tools.tweak", "force", 20,
3166                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3167                                                               1, 100, 1.0, 10.0,
3168                                                               labels, values, G_N_ELEMENTS(labels),
3169                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3170         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3171         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3172     }
3174     /* Mode */
3175     {
3176         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3178         GtkTreeIter iter;
3179         gtk_list_store_append( model, &iter );
3180         gtk_list_store_set( model, &iter,
3181                             0, _("Push mode"),
3182                             1, _("Push parts of paths in any direction"),
3183                             2, "tweak_push_mode",
3184                             -1 );
3186         gtk_list_store_append( model, &iter );
3187         gtk_list_store_set( model, &iter,
3188                             0, _("Shrink mode"),
3189                             1, _("Shrink (inset) parts of paths"),
3190                             2, "tweak_shrink_mode",
3191                             -1 );
3193         gtk_list_store_append( model, &iter );
3194         gtk_list_store_set( model, &iter,
3195                             0, _("Grow mode"),
3196                             1, _("Grow (outset) parts of paths"),
3197                             2, "tweak_grow_mode",
3198                             -1 );
3200         gtk_list_store_append( model, &iter );
3201         gtk_list_store_set( model, &iter,
3202                             0, _("Attract mode"),
3203                             1, _("Attract parts of paths towards cursor"),
3204                             2, "tweak_attract_mode",
3205                             -1 );
3207         gtk_list_store_append( model, &iter );
3208         gtk_list_store_set( model, &iter,
3209                             0, _("Repel mode"),
3210                             1, _("Repel parts of paths from cursor"),
3211                             2, "tweak_repel_mode",
3212                             -1 );
3214         gtk_list_store_append( model, &iter );
3215         gtk_list_store_set( model, &iter,
3216                             0, _("Roughen mode"),
3217                             1, _("Roughen parts of paths"),
3218                             2, "tweak_roughen_mode",
3219                             -1 );
3221         gtk_list_store_append( model, &iter );
3222         gtk_list_store_set( model, &iter,
3223                             0, _("Color paint mode"),
3224                             1, _("Paint the tool's color upon selected objects"),
3225                             2, "tweak_colorpaint_mode",
3226                             -1 );
3228         gtk_list_store_append( model, &iter );
3229         gtk_list_store_set( model, &iter,
3230                             0, _("Color jitter mode"),
3231                             1, _("Jitter the colors of selected objects"),
3232                             2, "tweak_colorjitter_mode",
3233                             -1 );
3235         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3236         g_object_set( act, "short_label", _("Mode:"), NULL );
3237         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3238         g_object_set_data( holder, "mode_action", act );
3240         ege_select_one_action_set_appearance( act, "full" );
3241         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3242         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3243         ege_select_one_action_set_icon_column( act, 2 );
3244         ege_select_one_action_set_tooltip_column( act, 1  );
3246         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3247         ege_select_one_action_set_active( act, mode );
3248         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3250         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3251     }
3253     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3255     {
3256         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3257         ege_output_action_set_use_markup( act, TRUE );
3258         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3259         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3260             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3261         g_object_set_data( holder, "tweak_channels_label", act);
3262     }
3264     {
3265         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3266                                                       _("Hue"),
3267                                                       _("In color mode, act on objects' hue"),
3268                                                       NULL,
3269                                                       Inkscape::ICON_SIZE_DECORATION );
3270         g_object_set( act, "short_label", _("H"), NULL );
3271         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3272         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3273         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3274         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3275             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3276         g_object_set_data( holder, "tweak_doh", act);
3277     }
3278     {
3279         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3280                                                       _("Saturation"),
3281                                                       _("In color mode, act on objects' saturation"),
3282                                                       NULL,
3283                                                       Inkscape::ICON_SIZE_DECORATION );
3284         g_object_set( act, "short_label", _("S"), NULL );
3285         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3286         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3287         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3288         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3289             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3290         g_object_set_data( holder, "tweak_dos", act );
3291     }
3292     {
3293         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3294                                                       _("Lightness"),
3295                                                       _("In color mode, act on objects' lightness"),
3296                                                       NULL,
3297                                                       Inkscape::ICON_SIZE_DECORATION );
3298         g_object_set( act, "short_label", _("L"), NULL );
3299         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3300         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3301         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3302         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3303             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3304         g_object_set_data( holder, "tweak_dol", act );
3305     }
3306     {
3307         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3308                                                       _("Opacity"),
3309                                                       _("In color mode, act on objects' opacity"),
3310                                                       NULL,
3311                                                       Inkscape::ICON_SIZE_DECORATION );
3312         g_object_set( act, "short_label", _("O"), NULL );
3313         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3314         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3315         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3316         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3317             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3318         g_object_set_data( holder, "tweak_doo", act );
3319     }
3321     {   /* Fidelity */
3322         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3323         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3324         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3325                                                               _("Fidelity"), _("Fidelity:"),
3326                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3327                                                               "tools.tweak", "fidelity", 50,
3328                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3329                                                               1, 100, 1.0, 10.0,
3330                                                               labels, values, G_N_ELEMENTS(labels),
3331                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3332         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3333         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3334         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3335             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3336         g_object_set_data( holder, "tweak_fidelity", eact );
3337     }
3340     /* Use Pressure button */
3341     {
3342         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3343                                                       _("Pressure"),
3344                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3345                                                       "use_pressure",
3346                                                       Inkscape::ICON_SIZE_DECORATION );
3347         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3348         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3349         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3350     }
3355 //########################
3356 //##     Calligraphy    ##
3357 //########################
3358 static void update_presets_list(GObject *dataKludge ){
3359     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3360     if (sel) {
3361         ege_select_one_action_set_active(sel, 0 );
3362     }
3365 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3367     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3368     update_presets_list(tbl);
3371 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3373     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3374     update_presets_list(tbl);
3377 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3379     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3380     update_presets_list(tbl);
3383 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3385     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3386     update_presets_list(tbl);
3389 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3391     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3392     update_presets_list(tbl);
3395 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3397     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3398     update_presets_list(tbl);
3401 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3403     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3404     update_presets_list(tbl);
3407 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3409     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3410     update_presets_list(tbl);
3413 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3415     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3416     update_presets_list(tbl);
3419 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3421     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3422     update_presets_list(tbl);
3425 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3427     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3428     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3429     update_presets_list(tbl);
3430     if (calligraphy_angle )
3431         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3435 #define PROFILE_FLOAT_SIZE 8
3436 #define PROFILE_BOOL_SIZE 3
3437 struct ProfileFloatElement {
3438     char const *name;
3439     double def;
3440     double min;
3441     double max;
3442 };
3443 struct ProfileBoolElement {
3444     char const *name;
3445     bool def;
3446 };
3448 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3449     {"mass",0.02, 0.0, 1.0},
3450     {"wiggle",0.0, 0.0, 1.0},
3451     {"angle",30.0, -90.0, 90.0},
3452     {"width",15.0, 1.0, 100.0},
3453     {"thinning",0.1, -1.0, 1.0},
3454     {"tremor",0.0, 0.0, 1.0},
3455     {"flatness",0.9, 0.0, 1.0},
3456     {"cap_rounding",0.0, 0.0, 5.0}
3457 };
3458 static ProfileBoolElement b_profile[PROFILE_BOOL_SIZE] = {
3459     {"usepressure",true},
3460     {"tracebackground",false},
3461     {"usetilt",true},
3462 };
3466 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3468     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset");
3469     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3470     gchar *profile_name = g_strdup_printf("Profile %d", new_index);
3471     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3473     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3474         ProfileFloatElement const &pe = f_profile[i];
3475         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3476         prefs_set_double_attribute(pref_path,pe.name,v);
3477         }    
3478     for (unsigned i = 0; i < PROFILE_BOOL_SIZE; ++i) {
3479         ProfileBoolElement const &pe = b_profile[i];
3480         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def?1:0 ,0,1);
3481         prefs_set_int_attribute(pref_path,pe.name,v);
3482         }
3483     prefs_set_string_attribute(pref_path,"name",profile_name);
3485     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3486     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3487     GtkTreeIter iter;
3488     gtk_list_store_append( model, &iter );
3489     gtk_list_store_set( model, &iter, 0, profile_name, 1, new_index, -1 );
3491     free(profile_id);
3492     free(profile_name);
3493     free(pref_path);
3496 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3497     
3498     gint preset_index = ege_select_one_action_get_active( act ); 
3499     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3500        
3501     if ( profile_name) {        
3502         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3503         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3504             ProfileFloatElement const &pe = f_profile[i];
3505             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3506             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3507             if ( adj ) {
3508                 gtk_adjustment_set_value(adj, v);
3509             }
3510         } 
3511         for (unsigned i = 0; i < PROFILE_BOOL_SIZE; ++i) {
3512             ProfileBoolElement const &pe = b_profile[i];
3513             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, 0, 1);
3514             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3515             if ( toggle ) {
3516                 gtk_toggle_action_set_active(toggle, v);
3517             } else printf("No toggle");
3518         }
3519         free(profile_name);
3520         g_object_set_data(dataKludge, "profile_selector",act); //restor selector visibility
3521     }
3526 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3528     {
3529         EgeAdjustmentAction* calligraphy_angle = 0;
3531         {
3532         /* Width */
3533         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3534         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3535         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3536                                                               _("Pen Width"), _("Width:"),
3537                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3538                                                               "tools.calligraphic", "width", 15,
3539                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3540                                                               1, 100, 1.0, 10.0,
3541                                                               labels, values, G_N_ELEMENTS(labels),
3542                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3543         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3544         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3545         }
3547         {
3548         /* Thinning */
3549             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3550             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3551         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3552                                                               _("Stroke Thinning"), _("Thinning:"),
3553                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3554                                                               "tools.calligraphic", "thinning", 0.1,
3555                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3556                                                               -1.0, 1.0, 0.01, 0.1,
3557                                                               labels, values, G_N_ELEMENTS(labels),
3558                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3559         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3560         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3561         }
3563         {
3564         /* Angle */
3565         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3566         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3567         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3568                                                               _("Pen Angle"), _("Angle:"),
3569                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3570                                                               "tools.calligraphic", "angle", 30,
3571                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3572                                                               -90.0, 90.0, 1.0, 10.0,
3573                                                               labels, values, G_N_ELEMENTS(labels),
3574                                                               sp_ddc_angle_value_changed, 1, 0 );
3575         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3576         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3577         g_object_set_data( holder, "angle", eact );
3578         calligraphy_angle = eact;
3579         }
3581         {
3582         /* Fixation */
3583             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3584         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3585         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3586                                                               _("Fixation"), _("Fixation:"),
3587                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3588                                                               "tools.calligraphic", "flatness", 0.9,
3589                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3590                                                               0.0, 1.0, 0.01, 0.1,
3591                                                               labels, values, G_N_ELEMENTS(labels),
3592                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3593         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3594         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3595         }
3597         {
3598         /* Cap Rounding */
3599             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3600         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3601         // TRANSLATORS: "cap" means "end" (both start and finish) here
3602         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3603                                                               _("Cap rounding"), _("Caps:"),
3604                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3605                                                               "tools.calligraphic", "cap_rounding", 0.0,
3606                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3607                                                               0.0, 5.0, 0.01, 0.1,
3608                                                               labels, values, G_N_ELEMENTS(labels),
3609                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3610         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3611         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3612         }
3614         {
3615         /* Tremor */
3616             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3617         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3618         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3619                                                               _("Stroke Tremor"), _("Tremor:"),
3620                                                               _("Increase to make strokes rugged and trembling"),
3621                                                               "tools.calligraphic", "tremor", 0.0,
3622                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3623                                                               0.0, 1.0, 0.01, 0.1,
3624                                                               labels, values, G_N_ELEMENTS(labels),
3625                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3627         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3628         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3629         }
3631         {
3632         /* Wiggle */
3633         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3634         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3635         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3636                                                               _("Pen Wiggle"), _("Wiggle:"),
3637                                                               _("Increase to make the pen waver and wiggle"),
3638                                                               "tools.calligraphic", "wiggle", 0.0,
3639                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3640                                                               0.0, 1.0, 0.01, 0.1,
3641                                                               labels, values, G_N_ELEMENTS(labels),
3642                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3643         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3644         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3645         }
3647         {
3648         /* Mass */
3649             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3650         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3651         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3652                                                               _("Pen Mass"), _("Mass:"),
3653                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3654                                                               "tools.calligraphic", "mass", 0.02,
3655                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3656                                                               0.0, 1.0, 0.01, 0.1,
3657                                                               labels, values, G_N_ELEMENTS(labels),
3658                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3659         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3660         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3661         }
3664         /* Trace Background button */
3665         {
3666             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3667                                                           _("Trace Background"),
3668                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3669                                                           "trace_background",
3670                                                           Inkscape::ICON_SIZE_DECORATION );
3671             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3672             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3673             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3674             g_object_set_data( holder, "tracebackground", act );
3675         }
3677         /* Use Pressure button */
3678         {
3679             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3680                                                           _("Pressure"),
3681                                                           _("Use the pressure of the input device to alter the width of the pen"),
3682                                                           "use_pressure",
3683                                                           Inkscape::ICON_SIZE_DECORATION );
3684             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3685             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3686             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3687             g_object_set_data( holder, "usepressure", act );
3688         }
3690         /* Use Tilt button */
3691         {
3692             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3693                                                           _("Tilt"),
3694                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3695                                                           "use_tilt",
3696                                                           Inkscape::ICON_SIZE_DECORATION );
3697             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3698             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3699             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3700             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3701             g_object_set_data( holder, "usetilt", act );
3702         }
3704         /*calligraphic profile */
3705         {
3706             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3707             gchar *pref_path;
3710             GtkTreeIter iter;
3711             gtk_list_store_append( model, &iter );
3712             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3714             //TODO: switch back to prefs API
3715             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3716             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3717             int ii=1;
3718             while (child_repr) {
3719                 GtkTreeIter iter;
3720                 char *preset_name = (char *) child_repr->attribute("name");
3721                 gtk_list_store_append( model, &iter );                
3722                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
3723                 child_repr = sp_repr_next(child_repr); 
3724             }
3726             pref_path = NULL;
3727             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3728             ege_select_one_action_set_appearance( act1, "compact" );
3729             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3730             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3731             g_object_set_data( holder, "profile_selector", act1 );
3732         }
3734         /*Save or delete calligraphic profile */
3735         {
3736             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3737                                              _("Defaults"),
3738                                              _("Save current settings as new profile"),
3739                                              GTK_STOCK_SAVE );
3740             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3741             gtk_action_group_add_action( mainActions, act );
3742             gtk_action_set_sensitive( act, TRUE );
3743             g_object_set_data( holder, "profile_save_delete", act );
3744         }
3745     }    
3749 //########################
3750 //##    Circle / Arc    ##
3751 //########################
3753 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3755     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3756     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3758     if (v1 == 0 && v2 == 0) {
3759         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3760             gtk_action_set_sensitive( ocb, FALSE );
3761             gtk_action_set_sensitive( make_whole, FALSE );
3762         }
3763     } else {
3764         gtk_action_set_sensitive( ocb, TRUE );
3765         gtk_action_set_sensitive( make_whole, TRUE );
3766     }
3769 static void
3770 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3772     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3774     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3775         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3776     }
3778     // quit if run by the attr_changed listener
3779     if (g_object_get_data( tbl, "freeze" )) {
3780         return;
3781     }
3783     // in turn, prevent listener from responding
3784     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3786     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3788     bool modmade = false;
3789     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3790          items != NULL;
3791          items = items->next)
3792     {
3793         SPItem *item = SP_ITEM(items->data);
3795         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3797             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3798             SPArc *arc = SP_ARC(item);
3800             if (!strcmp(value_name, "start"))
3801                 ge->start = (adj->value * M_PI)/ 180;
3802             else
3803                 ge->end = (adj->value * M_PI)/ 180;
3805             sp_genericellipse_normalize(ge);
3806             ((SPObject *)arc)->updateRepr();
3807             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3809             modmade = true;
3810         }
3811     }
3813     g_free(namespaced_name);
3815     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3817     sp_arctb_sensitivize( tbl, adj->value, other->value );
3819     if (modmade) {
3820         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3821                                    _("Arc: Change start/end"));
3822     }
3824     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3828 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3830     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3833 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3835     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3838 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3840     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3841     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3842         if ( ege_select_one_action_get_active( act ) != 0 ) {
3843             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3844         } else {
3845             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3846         }
3847     }
3849     // quit if run by the attr_changed listener
3850     if (g_object_get_data( tbl, "freeze" )) {
3851         return;
3852     }
3854     // in turn, prevent listener from responding
3855     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3857     bool modmade = false;
3859     if ( ege_select_one_action_get_active(act) != 0 ) {
3860         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3861              items != NULL;
3862              items = items->next)
3863         {
3864             if (SP_IS_ARC((SPItem *) items->data)) {
3865                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3866                 repr->setAttribute("sodipodi:open", "true");
3867                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3868                 modmade = true;
3869             }
3870         }
3871     } else {
3872         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3873              items != NULL;
3874              items = items->next)
3875         {
3876             if (SP_IS_ARC((SPItem *) items->data))    {
3877                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3878                 repr->setAttribute("sodipodi:open", NULL);
3879                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3880                 modmade = true;
3881             }
3882         }
3883     }
3885     if (modmade) {
3886         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3887                                    _("Arc: Change open/closed"));
3888     }
3890     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3893 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3895     GtkAdjustment *adj;
3896     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3897     gtk_adjustment_set_value(adj, 0.0);
3898     gtk_adjustment_value_changed(adj);
3900     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3901     gtk_adjustment_set_value(adj, 0.0);
3902     gtk_adjustment_value_changed(adj);
3904     spinbutton_defocus( GTK_OBJECT(obj) );
3907 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3908                                       gchar const */*old_value*/, gchar const */*new_value*/,
3909                                       bool /*is_interactive*/, gpointer data)
3911     GObject *tbl = G_OBJECT(data);
3913     // quit if run by the _changed callbacks
3914     if (g_object_get_data( tbl, "freeze" )) {
3915         return;
3916     }
3918     // in turn, prevent callbacks from responding
3919     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3921     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3922     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3924     GtkAdjustment *adj1,*adj2;
3925     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3926     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3927     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3928     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3930     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3932     char const *openstr = NULL;
3933     openstr = repr->attribute("sodipodi:open");
3934     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3936     if (openstr) {
3937         ege_select_one_action_set_active( ocb, 1 );
3938     } else {
3939         ege_select_one_action_set_active( ocb, 0 );
3940     }
3942     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3945 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3946     NULL, /* child_added */
3947     NULL, /* child_removed */
3948     arc_tb_event_attr_changed,
3949     NULL, /* content_changed */
3950     NULL  /* order_changed */
3951 };
3954 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3956     int n_selected = 0;
3957     Inkscape::XML::Node *repr = NULL;
3959     purge_repr_listener( tbl, tbl );
3961     for (GSList const *items = selection->itemList();
3962          items != NULL;
3963          items = items->next)
3964     {
3965         if (SP_IS_ARC((SPItem *) items->data)) {
3966             n_selected++;
3967             repr = SP_OBJECT_REPR((SPItem *) items->data);
3968         }
3969     }
3971     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3973     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3974     if (n_selected == 0) {
3975         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3976     } else if (n_selected == 1) {
3977         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3978         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3980         if (repr) {
3981             g_object_set_data( tbl, "repr", repr );
3982             Inkscape::GC::anchor(repr);
3983             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3984             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3985         }
3986     } else {
3987         // FIXME: implement averaging of all parameters for multiple selected
3988         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3989         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3990         sp_arctb_sensitivize( tbl, 1, 0 );
3991     }
3995 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3997     EgeAdjustmentAction* eact = 0;
4000     {
4001         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4002         ege_output_action_set_use_markup( act, TRUE );
4003         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4004         g_object_set_data( holder, "mode_action", act );
4005     }
4007     /* Start */
4008     {
4009         eact = create_adjustment_action( "ArcStartAction",
4010                                          _("Start"), _("Start:"),
4011                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4012                                          "tools.shapes.arc", "start", 0.0,
4013                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4014                                          -360.0, 360.0, 1.0, 10.0,
4015                                          0, 0, 0,
4016                                          sp_arctb_start_value_changed);
4017         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4018     }
4020     /* End */
4021     {
4022         eact = create_adjustment_action( "ArcEndAction",
4023                                          _("End"), _("End:"),
4024                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4025                                          "tools.shapes.arc", "end", 0.0,
4026                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4027                                          -360.0, 360.0, 1.0, 10.0,
4028                                          0, 0, 0,
4029                                          sp_arctb_end_value_changed);
4030         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4031     }
4033     /* Segments / Pie checkbox */
4034     {
4035         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4037         GtkTreeIter iter;
4038         gtk_list_store_append( model, &iter );
4039         gtk_list_store_set( model, &iter,
4040                             0, _("Closed arc"),
4041                             1, _("Switch to segment (closed shape with two radii)"),
4042                             2, "circle_closed_arc",
4043                             -1 );
4045         gtk_list_store_append( model, &iter );
4046         gtk_list_store_set( model, &iter,
4047                             0, _("Open Arc"),
4048                             1, _("Switch to arc (unclosed shape)"),
4049                             2, "circle_open_arc",
4050                             -1 );
4052         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4053         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4054         g_object_set_data( holder, "open_action", act );
4056         ege_select_one_action_set_appearance( act, "full" );
4057         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4058         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4059         ege_select_one_action_set_icon_column( act, 2 );
4060         ege_select_one_action_set_tooltip_column( act, 1  );
4062         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4063         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4064         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4065         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4066     }
4068     /* Make Whole */
4069     {
4070         InkAction* inky = ink_action_new( "ArcResetAction",
4071                                           _("Make whole"),
4072                                           _("Make the shape a whole ellipse, not arc or segment"),
4073                                           "reset_circle",
4074                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4075         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4076         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4077         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4078         g_object_set_data( holder, "make_whole", inky );
4079     }
4081     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4082     // sensitivize make whole and open checkbox
4083     {
4084         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4085         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4086         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4087     }
4090     sigc::connection *connection = new sigc::connection(
4091         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4092         );
4093     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4094     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4100 // toggle button callbacks and updaters
4102 //########################
4103 //##      Dropper       ##
4104 //########################
4106 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4107     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4108     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4109     if ( set_action ) {
4110         if ( gtk_toggle_action_get_active( act ) ) {
4111             gtk_action_set_sensitive( set_action, TRUE );
4112         } else {
4113             gtk_action_set_sensitive( set_action, FALSE );
4114         }
4115     }
4117     spinbutton_defocus(GTK_OBJECT(tbl));
4120 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4121     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4122     spinbutton_defocus(GTK_OBJECT(tbl));
4126 /**
4127  * Dropper auxiliary toolbar construction and setup.
4128  *
4129  * TODO: Would like to add swatch of current color.
4130  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4131  *       can drag and drop places. Will provide a nice mixing palette.
4132  */
4133 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4135     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4137     {
4138         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4139         ege_output_action_set_use_markup( act, TRUE );
4140         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4141     }
4143     {
4144         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4145                                                       _("Pick opacity"),
4146                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4147                                                       NULL,
4148                                                       Inkscape::ICON_SIZE_DECORATION );
4149         g_object_set( act, "short_label", _("Pick"), NULL );
4150         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4151         g_object_set_data( holder, "pick_action", act );
4152         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4153         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4154     }
4156     {
4157         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4158                                                       _("Assign opacity"),
4159                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4160                                                       NULL,
4161                                                       Inkscape::ICON_SIZE_DECORATION );
4162         g_object_set( act, "short_label", _("Assign"), NULL );
4163         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4164         g_object_set_data( holder, "set_action", act );
4165         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4166         // make sure it's disabled if we're not picking alpha
4167         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4168         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4169     }
4173 //########################
4174 //##    Text Toolbox    ##
4175 //########################
4176 /*
4177 static void
4178 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4180     //Call back for letter sizing spinbutton
4183 static void
4184 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4186     //Call back for line height spinbutton
4189 static void
4190 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4192     //Call back for horizontal kerning spinbutton
4195 static void
4196 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4198     //Call back for vertical kerning spinbutton
4201 static void
4202 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4204     //Call back for letter rotation spinbutton
4205 }*/
4207 namespace {
4209 bool popdown_visible = false;
4210 bool popdown_hasfocus = false;
4212 void
4213 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4215     SPStyle *query =
4216         sp_style_new (SP_ACTIVE_DOCUMENT);
4218     int result_fontspec =
4219         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4221     int result_family =
4222         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4224     int result_style =
4225         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4227     int result_numbers =
4228         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4230     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4232     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4233     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4234     {
4235         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4237         if (repr)
4238         {
4239             sp_style_read_from_repr (query, repr);
4240         }
4241         else
4242         {
4243             return;
4244         }
4245     }
4247     if (query->text)
4248     {
4249         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4250             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4251             gtk_entry_set_text (GTK_ENTRY (entry), "");
4253         } else if (query->text->font_specification.value || query->text->font_family.value) {
4255             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4257             // Get the font that corresponds
4258             Glib::ustring familyName;
4260             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4261             if (font) {
4262                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4263                 font->Unref();
4264                 font = NULL;
4265             }
4267             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4269             Gtk::TreePath path;
4270             try {
4271                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4272             } catch (...) {
4273                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4274                 return;
4275             }
4277             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4278             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4280             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4282             gtk_tree_selection_select_path (tselection, path.gobj());
4283             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4285             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4286         }
4288         //Size
4289         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4290         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4291         g_object_set_data (tbl, "size-block", gpointer(1));
4292         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4293         g_object_set_data (tbl, "size-block", gpointer(0));
4294         free (str);
4296         //Anchor
4297         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4298         {
4299             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4300             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4301             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4302             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4303         }
4304         else
4305         {
4306             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4307             {
4308                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4309                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4310                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4311                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4312             }
4313             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4314             {
4315                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4316                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4317                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4318                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4319             }
4320             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4321             {
4322                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4323                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4324                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4325                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4326             }
4327         }
4329         //Style
4330         {
4331             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4333             gboolean active = gtk_toggle_button_get_active (button);
4334             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4336             if (active != check)
4337             {
4338                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4339                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4340                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4341             }
4342         }
4344         {
4345             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4347             gboolean active = gtk_toggle_button_get_active (button);
4348             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4350             if (active != check)
4351             {
4352                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4353                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4354                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4355             }
4356         }
4358         //Orientation
4359         //locking both buttons, changing one affect all group (both)
4360         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4361         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4363         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4364         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4366         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4367         {
4368             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4369         }
4370         else
4371         {
4372             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4373         }
4374         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4375         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4376     }
4378     sp_style_unref(query);
4381 void
4382 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4384     sp_text_toolbox_selection_changed (selection, tbl);
4387 void
4388 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4390     sp_text_toolbox_selection_changed (NULL, tbl);
4393 void
4394 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4395                                 GObject             *tbl)
4397     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4398     GtkTreeModel *model = 0;
4399     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4400     GtkTreeIter   iter;
4401     char         *family = 0;
4403     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4404     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4406     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4407         return;
4408     }
4410     gtk_tree_model_get (model, &iter, 0, &family, -1);
4412     if (g_object_get_data (G_OBJECT (selection), "block"))
4413     {
4414         gtk_entry_set_text (GTK_ENTRY (entry), family);
4415         return;
4416     }
4418     gtk_entry_set_text (GTK_ENTRY (entry), family);
4420     SPStyle *query =
4421         sp_style_new (SP_ACTIVE_DOCUMENT);
4423     int result_fontspec =
4424         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4426     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4428     SPCSSAttr *css = sp_repr_css_attr_new ();
4431     // First try to get the font spec from the stored value
4432     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4434     if (fontSpec.empty()) {
4435         // Construct a new font specification if it does not yet exist
4436         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4437         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4438         fontFromStyle->Unref();
4439     }
4441     if (!fontSpec.empty()) {
4442         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4443         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4444             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4445             if (font) {
4446                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4448                 // Set all the these just in case they were altered when finding the best
4449                 // match for the new family and old style...
4451                 gchar c[256];
4453                 font->Family(c, 256);
4454                 sp_repr_css_set_property (css, "font-family", c);
4456                 font->Attribute( "weight", c, 256);
4457                 sp_repr_css_set_property (css, "font-weight", c);
4459                 font->Attribute("style", c, 256);
4460                 sp_repr_css_set_property (css, "font-style", c);
4462                 font->Attribute("stretch", c, 256);
4463                 sp_repr_css_set_property (css, "font-stretch", c);
4465                 font->Attribute("variant", c, 256);
4466                 sp_repr_css_set_property (css, "font-variant", c);
4468                 font->Unref();
4469             }
4470         }
4471     }
4473     // If querying returned nothing, set the default style of the tool (for new texts)
4474     if (result_fontspec == QUERY_STYLE_NOTHING)
4475     {
4476         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4477         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4478     }
4479     else
4480     {
4481         sp_desktop_set_style (desktop, css, true, true);
4482     }
4484     sp_style_unref(query);
4486     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4487                                    _("Text: Change font family"));
4488     sp_repr_css_attr_unref (css);
4489     free (family);
4490     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4492     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4495 void
4496 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4497                                        GObject      *tbl)
4499     const char *family = gtk_entry_get_text (entry);
4501     try {
4502         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4503         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4504         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4505         gtk_tree_selection_select_path (selection, path.gobj());
4506         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4507         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4508     } catch (...) {
4509         if (family && strlen (family))
4510         {
4511             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4512         }
4513     }
4516 void
4517 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4518                                    gpointer          data)
4520     if (g_object_get_data (G_OBJECT (button), "block")) return;
4521     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4522     int prop = GPOINTER_TO_INT(data);
4524     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4525     SPCSSAttr *css = sp_repr_css_attr_new ();
4527     switch (prop)
4528     {
4529         case 0:
4530         {
4531             sp_repr_css_set_property (css, "text-anchor", "start");
4532             sp_repr_css_set_property (css, "text-align", "start");
4533             break;
4534         }
4535         case 1:
4536         {
4537             sp_repr_css_set_property (css, "text-anchor", "middle");
4538             sp_repr_css_set_property (css, "text-align", "center");
4539             break;
4540         }
4542         case 2:
4543         {
4544             sp_repr_css_set_property (css, "text-anchor", "end");
4545             sp_repr_css_set_property (css, "text-align", "end");
4546             break;
4547         }
4549         case 3:
4550         {
4551             sp_repr_css_set_property (css, "text-anchor", "start");
4552             sp_repr_css_set_property (css, "text-align", "justify");
4553             break;
4554         }
4555     }
4557     SPStyle *query =
4558         sp_style_new (SP_ACTIVE_DOCUMENT);
4559     int result_numbers =
4560         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4562     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4563     if (result_numbers == QUERY_STYLE_NOTHING)
4564     {
4565         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4566     }
4568     sp_style_unref(query);
4570     sp_desktop_set_style (desktop, css, true, true);
4571     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4572                                    _("Text: Change alignment"));
4573     sp_repr_css_attr_unref (css);
4575     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4578 void
4579 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4580                                gpointer          data)
4582     if (g_object_get_data (G_OBJECT (button), "block")) return;
4584     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4585     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4586     int          prop       = GPOINTER_TO_INT(data);
4587     bool         active     = gtk_toggle_button_get_active (button);
4589     SPStyle *query =
4590         sp_style_new (SP_ACTIVE_DOCUMENT);
4592     int result_fontspec =
4593         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4595     int result_family =
4596         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4598     int result_style =
4599         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4601     int result_numbers =
4602         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4604     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4605     Glib::ustring newFontSpec = "";
4607     if (fontSpec.empty()) {
4608         // Construct a new font specification if it does not yet exist
4609         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4610         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4611         fontFromStyle->Unref();
4612     }
4614     switch (prop)
4615     {
4616         case 0:
4617         {
4618             if (!fontSpec.empty()) {
4619                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4620             }
4621             if (fontSpec != newFontSpec) {
4622                 // Don't even set the bold if the font didn't exist on the system
4623                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4624             }
4625             break;
4626         }
4628         case 1:
4629         {
4630             if (!fontSpec.empty()) {
4631                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4632             }
4633             if (fontSpec != newFontSpec) {
4634                 // Don't even set the italic if the font didn't exist on the system
4635                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4636             }
4637             break;
4638         }
4639     }
4641     if (!newFontSpec.empty()) {
4642         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4643     }
4645     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4646     if (result_fontspec == QUERY_STYLE_NOTHING)
4647     {
4648         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4649     }
4651     sp_style_unref(query);
4653     sp_desktop_set_style (desktop, css, true, true);
4654     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4655                                    _("Text: Change font style"));
4656     sp_repr_css_attr_unref (css);
4658     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4661 void
4662 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4663                                      gpointer         data)
4665     if (g_object_get_data (G_OBJECT (button), "block")) {
4666         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4667         return;
4668     }
4670     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4671     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4672     int          prop       = GPOINTER_TO_INT(data);
4674     switch (prop)
4675     {
4676         case 0:
4677         {
4678             sp_repr_css_set_property (css, "writing-mode", "lr");
4679             break;
4680         }
4682         case 1:
4683         {
4684             sp_repr_css_set_property (css, "writing-mode", "tb");
4685             break;
4686         }
4687     }
4689     SPStyle *query =
4690         sp_style_new (SP_ACTIVE_DOCUMENT);
4691     int result_numbers =
4692         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4694     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4695     if (result_numbers == QUERY_STYLE_NOTHING)
4696     {
4697         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4698     }
4700     sp_desktop_set_style (desktop, css, true, true);
4701     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4702                                    _("Text: Change orientation"));
4703     sp_repr_css_attr_unref (css);
4705     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4708 gboolean
4709 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4711     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4712     if (!desktop) return FALSE;
4714     switch (get_group0_keyval (event)) {
4715         case GDK_Escape: // defocus
4716             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4717             sp_text_toolbox_selection_changed (NULL, tbl); // update
4718             return TRUE; // I consumed the event
4719             break;
4720     }
4721     return FALSE;
4724 gboolean
4725 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4727     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4728     if (!desktop) return FALSE;
4730     switch (get_group0_keyval (event)) {
4731         case GDK_KP_Enter:
4732         case GDK_Return:
4733         case GDK_Escape: // defocus
4734             gtk_widget_hide (w);
4735             popdown_visible = false;
4736             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4737             return TRUE; // I consumed the event
4738             break;
4739         case GDK_w:
4740         case GDK_W:
4741             if (event->state & GDK_CONTROL_MASK) {
4742                 gtk_widget_hide (w);
4743                 popdown_visible = false;
4744                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4745                 return TRUE; // I consumed the event
4746             }
4747             break;
4748     }
4749     return FALSE;
4753 void
4754 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4755                                GObject     *tbl)
4757     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4759     if (g_object_get_data (tbl, "size-block")) return;
4761     // If this is not from selecting a size in the list (in which case get_active will give the
4762     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4763     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4764     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4765     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4766         return;
4768     gchar *endptr;
4769     gdouble value = -1;
4770     char *text = gtk_combo_box_get_active_text (cbox);
4771     if (text) {
4772         value = g_strtod (text, &endptr);
4773         if (endptr == text) // conversion failed, non-numeric input
4774             value = -1;
4775         free (text);
4776     }
4777     if (value <= 0) {
4778         return; // could not parse value
4779     }
4781     SPCSSAttr *css = sp_repr_css_attr_new ();
4782     Inkscape::CSSOStringStream osfs;
4783     osfs << value;
4784     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4786     SPStyle *query =
4787         sp_style_new (SP_ACTIVE_DOCUMENT);
4788     int result_numbers =
4789         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4791     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4792     if (result_numbers == QUERY_STYLE_NOTHING)
4793     {
4794         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4795     }
4797     sp_style_unref(query);
4799     sp_desktop_set_style (desktop, css, true, true);
4800     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4801                                    _("Text: Change font size"));
4802     sp_repr_css_attr_unref (css);
4804     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4807 gboolean
4808 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4810     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4811     if (!desktop) return FALSE;
4813     if (!g_object_get_data (tbl, "esc-pressed")) {
4814         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4815         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4816         sp_text_toolbox_size_changed (cbox, tbl);
4817         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4818     }
4819     return FALSE; // I consumed the event
4823 gboolean
4824 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4826     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4827     if (!desktop) return FALSE;
4829     switch (get_group0_keyval (event)) {
4830         case GDK_Escape: // defocus
4831             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4832             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4833             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4834             return TRUE; // I consumed the event
4835             break;
4836         case GDK_Return: // defocus
4837         case GDK_KP_Enter:
4838             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4839             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4840             sp_text_toolbox_size_changed (cbox, tbl);
4841             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4842             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4843             return TRUE; // I consumed the event
4844             break;
4845     }
4846     return FALSE;
4849 void
4850 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4851                                          GObject            *tbl)
4853     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4854     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4855     int x, y;
4857     if (!popdown_visible)
4858     {
4859         gdk_window_get_origin (widget->window, &x, &y);
4860         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4861         gtk_widget_show_all (popdown);
4862         //sp_transientize (popdown);
4864         gdk_pointer_grab (widget->window, TRUE,
4865                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4866                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4867                                         GDK_POINTER_MOTION_MASK),
4868                           NULL, NULL, GDK_CURRENT_TIME);
4870         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4872         popdown_visible = true;
4873     }
4874     else
4875     {
4876         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4877         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4878         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4879         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4880         gtk_widget_hide (popdown);
4881         popdown_visible = false;
4882     }
4885 gboolean
4886 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4887                                  GdkEventFocus    */*event*/,
4888                                  GObject          */*tbl*/)
4890     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4891     return FALSE;
4894 gboolean
4895 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4896                                    GdkEventFocus    */*event*/,
4897                                    GObject          */*tbl*/)
4899     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4901     if (popdown_hasfocus) {
4902         gtk_widget_hide (popdown);
4903         popdown_hasfocus = false;
4904         popdown_visible = false;
4905         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4906         return TRUE;
4907     }
4908     return FALSE;
4911 gboolean
4912 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
4913                                    GdkEventFocus    */*event*/,
4914                                    GObject          */*tbl*/)
4916     popdown_hasfocus = true;
4917     return TRUE;
4921 void
4922 cell_data_func  (GtkTreeViewColumn */*column*/,
4923                  GtkCellRenderer   *cell,
4924                  GtkTreeModel      *tree_model,
4925                  GtkTreeIter       *iter,
4926                  gpointer           /*data*/)
4928     char        *family,
4929         *family_escaped,
4930         *sample_escaped;
4932     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4934     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4936     family_escaped = g_markup_escape_text (family, -1);
4937     sample_escaped = g_markup_escape_text (sample, -1);
4939     std::stringstream markup;
4940     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4941     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4943     free (family);
4944     free (family_escaped);
4945     free (sample_escaped);
4948 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4949     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4950     if (completion) {
4951         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4952         g_object_unref (completion);
4953     }
4956 GtkWidget*
4957 sp_text_toolbox_new (SPDesktop *desktop)
4959     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4961     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4962     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4964     GtkTooltips *tt = gtk_tooltips_new();
4965     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4967     ////////////Family
4968     //Window
4969     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4970     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4972     //Entry
4973     GtkWidget           *entry = gtk_entry_new ();
4974     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4975     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4976     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4977     gtk_entry_completion_set_text_column (completion, 0);
4978     gtk_entry_completion_set_minimum_key_length (completion, 1);
4979     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4980     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4981     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4982     aux_toolbox_space (tbl, 1);
4983     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4984     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4986     //Button
4987     GtkWidget   *button = gtk_button_new ();
4988     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4989     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4991     //Popdown
4992     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4993     GtkWidget           *treeview = gtk_tree_view_new ();
4995     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4996     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4997     gtk_tree_view_column_pack_start (column, cell, FALSE);
4998     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4999     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5000     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5002     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5003     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5004     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5006     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5008     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5009     gtk_container_add (GTK_CONTAINER (sw), treeview);
5011     gtk_container_add (GTK_CONTAINER (window), sw);
5012     gtk_widget_set_size_request (window, 300, 450);
5014     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5015     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5016     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5018     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5020     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5021     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5022     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5024     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5025     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5027     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5028     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5029     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5030     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5031     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5033     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
5034     aux_toolbox_space (tbl, 1);
5035     GtkWidget *box = gtk_event_box_new ();
5036     gtk_container_add (GTK_CONTAINER (box), image);
5037     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5038     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5039     GtkTooltips *tooltips = gtk_tooltips_new ();
5040     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5041     gtk_widget_hide (GTK_WIDGET (box));
5042     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5044     ////////////Size
5045     const char *sizes[] = {
5046         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5047         "16", "18", "20", "22", "24", "28",
5048         "32", "36", "40", "48", "56", "64", "72", "144"
5049     };
5051     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5052     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5053     gtk_widget_set_size_request (cbox, 80, -1);
5054     aux_toolbox_space (tbl, 1);
5055     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5056     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5057     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5058     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5059     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5061     //spacer
5062     aux_toolbox_space (tbl, 4);
5063     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5065     ////////////Text anchor
5066     GtkWidget *group   = gtk_radio_button_new (NULL);
5067     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5068     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5070     // left
5071     GtkWidget *rbutton = group;
5072     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5073     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5074     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5076     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5077     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5078     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5079     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5081     // center
5082     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5083     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5084     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
5085     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5087     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5088     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5089     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5090     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5092     // right
5093     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5094     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5095     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5096     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5098     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5099     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5100     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5101     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5103     // fill
5104     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5105     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5106     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5107     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5109     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5110     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5111     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5112     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5114     aux_toolbox_space (tbl, 1);
5115     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5117     //spacer
5118     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5120     ////////////Text style
5121     row = gtk_hbox_new (FALSE, 4);
5123     // bold
5124     rbutton = gtk_toggle_button_new ();
5125     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5126     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5127     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5128     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5130     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5131     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5132     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5134     // italic
5135     rbutton = gtk_toggle_button_new ();
5136     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5137     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5138     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5139     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5141     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5142     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5143     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5145     aux_toolbox_space (tbl, 1);
5146     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5148     //spacer
5149     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5151     ////////////Text orientation
5152     group   = gtk_radio_button_new (NULL);
5153     row     = gtk_hbox_new (FALSE, 4);
5154     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5156     // horizontal
5157     rbutton = group;
5158     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5159     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5160     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5161     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5163     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5164     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5165     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5167     // vertical
5168     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5169     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5170     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5171     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5172     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5174     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5175     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5176     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5177     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5180     //watch selection
5181     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5183     sigc::connection *c_selection_changed =
5184         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5185                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5186     pool->add_connection ("selection-changed", c_selection_changed);
5188     sigc::connection *c_selection_modified =
5189         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5190                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5191     pool->add_connection ("selection-modified", c_selection_modified);
5193     sigc::connection *c_subselection_changed =
5194         new sigc::connection (desktop->connectToolSubselectionChanged
5195                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5196     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5198     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5201     gtk_widget_show_all (tbl);
5202     return tbl;
5204 } // end of sp_text_toolbox_new()
5206 }//<unnamed> namespace
5209 //#########################
5210 //##      Connector      ##
5211 //#########################
5213 static void sp_connector_path_set_avoid(void)
5215     cc_selection_set_avoid(true);
5219 static void sp_connector_path_set_ignore(void)
5221     cc_selection_set_avoid(false);
5226 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5228     // quit if run by the _changed callbacks
5229     if (g_object_get_data( tbl, "freeze" )) {
5230         return;
5231     }
5233     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5234     SPDocument *doc = sp_desktop_document(desktop);
5236     if (!sp_document_get_undo_sensitive(doc))
5237     {
5238         return;
5239     }
5241     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5243     if ( repr->attribute("inkscape:connector-spacing") ) {
5244         gdouble priorValue = gtk_adjustment_get_value(adj);
5245         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5246         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5247             return;
5248         }
5249     } else if ( adj->value == defaultConnSpacing ) {
5250         return;
5251     }
5253     // in turn, prevent callbacks from responding
5254     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5256     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5257     SP_OBJECT(desktop->namedview)->updateRepr();
5259     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5260     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5261         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5262         NR::Matrix m = NR::identity();
5263         avoid_item_move(&m, item);
5264     }
5266     if (items) {
5267         g_slist_free(items);
5268     }
5270     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5271             _("Change connector spacing"));
5273     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5275     spinbutton_defocus(GTK_OBJECT(tbl));
5278 static void sp_connector_graph_layout(void)
5280     if (!SP_ACTIVE_DESKTOP) return;
5282     // hack for clones, see comment in align-and-distribute.cpp
5283     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5284     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5286     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5288     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5290     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5293 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5295     if ( gtk_toggle_action_get_active( act ) ) {
5296         prefs_set_string_attribute("tools.connector", "directedlayout",
5297                 "true");
5298     } else {
5299         prefs_set_string_attribute("tools.connector", "directedlayout",
5300                 "false");
5301     }
5304 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5306     if ( gtk_toggle_action_get_active( act ) ) {
5307         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5308                 "true");
5309     } else {
5310         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5311                 "false");
5312     }
5316 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5318     prefs_set_double_attribute("tools.connector", "length", adj->value);
5319     spinbutton_defocus(GTK_OBJECT(tbl));
5322 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5323                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5324                                             bool /*is_interactive*/, gpointer data)
5326     GtkWidget *tbl = GTK_WIDGET(data);
5328     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5329         return;
5330     }
5331     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5332         return;
5333     }
5335     GtkAdjustment *adj = (GtkAdjustment*)
5336             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5337     gdouble spacing = defaultConnSpacing;
5338     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5340     gtk_adjustment_set_value(adj, spacing);
5344 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5345     NULL, /* child_added */
5346     NULL, /* child_removed */
5347     connector_tb_event_attr_changed,
5348     NULL, /* content_changed */
5349     NULL  /* order_changed */
5350 };
5353 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5355     {
5356         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5357                                           _("Avoid"),
5358                                           _("Make connectors avoid selected objects"),
5359                                           "connector_avoid",
5360                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5361         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5362         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5363     }
5365     {
5366         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5367                                           _("Ignore"),
5368                                           _("Make connectors ignore selected objects"),
5369                                           "connector_ignore",
5370                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5371         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5372         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5373     }
5375     EgeAdjustmentAction* eact = 0;
5377     // Spacing spinbox
5378     eact = create_adjustment_action( "ConnectorSpacingAction",
5379                                      _("Connector Spacing"), _("Spacing:"),
5380                                      _("The amount of space left around objects by auto-routing connectors"),
5381                                      "tools.connector", "spacing", defaultConnSpacing,
5382                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5383                                      0, 100, 1.0, 10.0,
5384                                      0, 0, 0,
5385                                      connector_spacing_changed, 1, 0 );
5386     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5388     // Graph (connector network) layout
5389     {
5390         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5391                                           _("Graph"),
5392                                           _("Nicely arrange selected connector network"),
5393                                           "graph_layout",
5394                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5395         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5396         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5397     }
5399     // Default connector length spinbox
5400     eact = create_adjustment_action( "ConnectorLengthAction",
5401                                      _("Connector Length"), _("Length:"),
5402                                      _("Ideal length for connectors when layout is applied"),
5403                                      "tools.connector", "length", 100,
5404                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5405                                      10, 1000, 10.0, 100.0,
5406                                      0, 0, 0,
5407                                      connector_length_changed, 1, 0 );
5408     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5411     // Directed edges toggle button
5412     {
5413         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5414                                                       _("Downwards"),
5415                                                       _("Make connectors with end-markers (arrows) point downwards"),
5416                                                       "directed_graph",
5417                                                       Inkscape::ICON_SIZE_DECORATION );
5418         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5420         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5421         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5422                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5424         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5425     }
5427     // Avoid overlaps toggle button
5428     {
5429         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5430                                                       _("Remove overlaps"),
5431                                                       _("Do not allow overlapping shapes"),
5432                                                       "remove_overlaps",
5433                                                       Inkscape::ICON_SIZE_DECORATION );
5434         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5436         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5437         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5438                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5440         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5441     }
5443     // Code to watch for changes to the connector-spacing attribute in
5444     // the XML.
5445     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5446     g_assert(repr != NULL);
5448     purge_repr_listener( holder, holder );
5450     if (repr) {
5451         g_object_set_data( holder, "repr", repr );
5452         Inkscape::GC::anchor(repr);
5453         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5454         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5455     }
5456 } // end of sp_connector_toolbox_prep()
5459 //#########################
5460 //##     Paintbucket     ##
5461 //#########################
5463 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5465     gint channels = ege_select_one_action_get_active( act );
5466     flood_channels_set_channels( channels );
5469 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5471     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5474 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5476     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5479 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5481     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5482     SPUnit const *unit = tracker->getActiveUnit();
5484     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5486     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5489 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5491     // FIXME: make defaults settable via Inkscape Options
5492     struct KeyValue {
5493         char const *key;
5494         double value;
5495     } const key_values[] = {
5496         {"threshold", 15},
5497         {"offset", 0.0}
5498     };
5500     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5501         KeyValue const &kv = key_values[i];
5502         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5503         if ( adj ) {
5504             gtk_adjustment_set_value(adj, kv.value);
5505         }
5506     }
5508     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5509     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5510     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5511     ege_select_one_action_set_active( autogap_action, 0 );
5514 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5516     EgeAdjustmentAction* eact = 0;
5518     {
5519         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5521         GList* items = 0;
5522         gint count = 0;
5523         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5524         {
5525             GtkTreeIter iter;
5526             gtk_list_store_append( model, &iter );
5527             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5528             count++;
5529         }
5530         g_list_free( items );
5531         items = 0;
5532         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5533         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5534         ege_select_one_action_set_appearance( act1, "compact" );
5535         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5536         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5537         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5538         g_object_set_data( holder, "channels_action", act1 );
5539     }
5541     // Spacing spinbox
5542     {
5543         eact = create_adjustment_action(
5544             "ThresholdAction",
5545             _("Fill Threshold"), _("Threshold:"),
5546             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5547             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5548             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5549             0, 0, 0,
5550             paintbucket_threshold_changed, 1, 0 );
5552         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5553     }
5555     // Create the units menu.
5556     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5557     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5558     if (stored_unit)
5559         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5560     g_object_set_data( holder, "tracker", tracker );
5561     {
5562         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5563         gtk_action_group_add_action( mainActions, act );
5564     }
5566     // Offset spinbox
5567     {
5568         eact = create_adjustment_action(
5569             "OffsetAction",
5570             _("Grow/shrink by"), _("Grow/shrink by:"),
5571             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5572             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5573             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5574             0, 0, 0,
5575             paintbucket_offset_changed, 1, 2);
5576         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5578         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5579     }
5581     /* Auto Gap */
5582     {
5583         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5585         GList* items = 0;
5586         gint count = 0;
5587         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5588         {
5589             GtkTreeIter iter;
5590             gtk_list_store_append( model, &iter );
5591             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5592             count++;
5593         }
5594         g_list_free( items );
5595         items = 0;
5596         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5597         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5598         ege_select_one_action_set_appearance( act2, "compact" );
5599         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5600         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5601         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5602         g_object_set_data( holder, "autogap_action", act2 );
5603     }
5605     /* Reset */
5606     {
5607         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5608                                           _("Defaults"),
5609                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5610                                           GTK_STOCK_CLEAR );
5611         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5612         gtk_action_group_add_action( mainActions, act );
5613         gtk_action_set_sensitive( act, TRUE );
5614     }
5618 /*
5619   Local Variables:
5620   mode:c++
5621   c-file-style:"stroustrup"
5622   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5623   indent-tabs-mode:nil
5624   fill-column:99
5625   End:
5626 */
5627 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :