Code

Initial cut of eraser tool
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "sp-clippath.h"
78 #include "sp-mask.h"
79 #include "style.h"
80 #include "selection.h"
81 #include "selection-chemistry.h"
82 #include "document-private.h"
83 #include "desktop-style.h"
84 #include "../libnrtype/font-lister.h"
85 #include "../libnrtype/font-instance.h"
86 #include "../connection-pool.h"
87 #include "../prefs-utils.h"
88 #include "../inkscape-stock.h"
89 #include "icon.h"
90 #include "graphlayout/graphlayout.h"
92 #include "mod360.h"
94 #include "toolbox.h"
96 #include "flood-context.h"
98 #include "ink-action.h"
99 #include "ege-adjustment-action.h"
100 #include "ege-output-action.h"
101 #include "ege-select-one-action.h"
102 #include "helper/unit-tracker.h"
104 #include "svg/css-ostringstream.h"
106 #include "widgets/calligraphic-profile-rename.h"
108 using Inkscape::UnitTracker;
110 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
111 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
113 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
126 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
133 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
134     static Inkscape::IconSize sizeChoices[] = {
135         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
136         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
137         Inkscape::ICON_SIZE_MENU
138     };
139     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
140     return sizeChoices[index];
143 static struct {
144     gchar const *type_name;
145     gchar const *data_name;
146     sp_verb_t verb;
147     sp_verb_t doubleclick_verb;
148 } const tools[] = {
149     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
150     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
151     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
152     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
153     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
154     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
155     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
156     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
157     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
158     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
159     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
160     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
161     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
162     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
163     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
164     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
165     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
166     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
167     { NULL, NULL, 0, 0 }
168 };
170 static struct {
171     gchar const *type_name;
172     gchar const *data_name;
173     GtkWidget *(*create_func)(SPDesktop *desktop);
174     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
175     gchar const *ui_name;
176     gint swatch_verb_id;
177     gchar const *swatch_tool;
178     gchar const *swatch_tip;
179 } const aux_toolboxes[] = {
180     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
181       SP_VERB_INVALID, 0, 0},
182     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
183       SP_VERB_INVALID, 0, 0},
184     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
185       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
186     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
187       SP_VERB_INVALID, 0, 0},
188     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
189       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
190     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
191       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
192     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
193       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
194     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
195       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
196     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
197       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
198     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
199       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
200     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
201       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
202     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
203       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
204     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
205       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
206     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
207       SP_VERB_INVALID, 0, 0},
208     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
209       SP_VERB_INVALID, 0, 0},
210     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
211       SP_VERB_INVALID, 0, 0},
212     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
213       SP_VERB_INVALID, 0, 0},
214     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
215       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
216     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
217 };
220 static gchar const * ui_descr =
221         "<ui>"
222         "  <toolbar name='SelectToolbar'>"
223         "    <toolitem action='EditSelectAll' />"
224         "    <toolitem action='EditSelectAllInAllLayers' />"
225         "    <toolitem action='EditDeselect' />"
226         "    <separator />"
227         "    <toolitem action='ObjectRotate90CCW' />"
228         "    <toolitem action='ObjectRotate90' />"
229         "    <toolitem action='ObjectFlipHorizontally' />"
230         "    <toolitem action='ObjectFlipVertically' />"
231         "    <separator />"
232         "    <toolitem action='SelectionToBack' />"
233         "    <toolitem action='SelectionLower' />"
234         "    <toolitem action='SelectionRaise' />"
235         "    <toolitem action='SelectionToFront' />"
236         "    <separator />"
237         "    <toolitem action='XAction' />"
238         "    <toolitem action='YAction' />"
239         "    <toolitem action='WidthAction' />"
240         "    <toolitem action='LockAction' />"
241         "    <toolitem action='HeightAction' />"
242         "    <toolitem action='UnitsAction' />"
243         "    <separator />"
244         "    <toolitem action='transform_affect_label' />"
245         "    <toolitem action='transform_stroke' />"
246         "    <toolitem action='transform_corners' />"
247         "    <toolitem action='transform_gradient' />"
248         "    <toolitem action='transform_pattern' />"
249         "  </toolbar>"
251         "  <toolbar name='NodeToolbar'>"
252         "    <toolitem action='NodeInsertAction' />"
253         "    <toolitem action='NodeDeleteAction' />"
254         "    <separator />"
255         "    <toolitem action='NodeJoinAction' />"
256         "    <toolitem action='NodeBreakAction' />"
257         "    <separator />"
258         "    <toolitem action='NodeJoinSegmentAction' />"
259         "    <toolitem action='NodeDeleteSegmentAction' />"
260         "    <separator />"
261         "    <toolitem action='NodeCuspAction' />"
262         "    <toolitem action='NodeSmoothAction' />"
263         "    <toolitem action='NodeSymmetricAction' />"
264         "    <separator />"
265         "    <toolitem action='NodeLineAction' />"
266         "    <toolitem action='NodeCurveAction' />"
267         "    <separator />"
268         "    <toolitem action='ObjectToPath' />"
269         "    <toolitem action='StrokeToPath' />"
270         "    <separator />"
271         "    <toolitem action='NodeXAction' />"
272         "    <toolitem action='NodeYAction' />"
273         "    <toolitem action='NodeUnitsAction' />"
274         "    <separator />"
275         "    <toolitem action='ObjectEditClipPathAction' />"
276         "    <toolitem action='ObjectEditMaskPathAction' />"
277         "    <toolitem action='EditNextLPEParameterAction' />"
278         "    <separator />"
279         "    <toolitem action='NodesShowHandlesAction' />"
280         "    <toolitem action='NodesShowHelperpath' />"
281         "  </toolbar>"
283         "  <toolbar name='TweakToolbar'>"
284         "    <toolitem action='TweakWidthAction' />"
285         "    <separator />"
286         "    <toolitem action='TweakForceAction' />"
287         "    <toolitem action='TweakPressureAction' />"
288         "    <separator />"
289         "    <toolitem action='TweakModeAction' />"
290         "    <separator />"
291         "    <toolitem action='TweakFidelityAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakChannelsLabel' />"
294         "    <toolitem action='TweakDoH' />"
295         "    <toolitem action='TweakDoS' />"
296         "    <toolitem action='TweakDoL' />"
297         "    <toolitem action='TweakDoO' />"
298         "  </toolbar>"
300         "  <toolbar name='ZoomToolbar'>"
301         "    <toolitem action='ZoomIn' />"
302         "    <toolitem action='ZoomOut' />"
303         "    <separator />"
304         "    <toolitem action='Zoom1:0' />"
305         "    <toolitem action='Zoom1:2' />"
306         "    <toolitem action='Zoom2:1' />"
307         "    <separator />"
308         "    <toolitem action='ZoomSelection' />"
309         "    <toolitem action='ZoomDrawing' />"
310         "    <toolitem action='ZoomPage' />"
311         "    <toolitem action='ZoomPageWidth' />"
312         "    <separator />"
313         "    <toolitem action='ZoomPrev' />"
314         "    <toolitem action='ZoomNext' />"
315         "  </toolbar>"
317         "  <toolbar name='StarToolbar'>"
318         "    <separator />"
319         "    <toolitem action='StarStateAction' />"
320         "    <separator />"
321         "    <toolitem action='FlatAction' />"
322         "    <separator />"
323         "    <toolitem action='MagnitudeAction' />"
324         "    <toolitem action='SpokeAction' />"
325         "    <toolitem action='RoundednessAction' />"
326         "    <toolitem action='RandomizationAction' />"
327         "    <separator />"
328         "    <toolitem action='StarResetAction' />"
329         "  </toolbar>"
331         "  <toolbar name='RectToolbar'>"
332         "    <toolitem action='RectStateAction' />"
333         "    <toolitem action='RectWidthAction' />"
334         "    <toolitem action='RectHeightAction' />"
335         "    <toolitem action='RadiusXAction' />"
336         "    <toolitem action='RadiusYAction' />"
337         "    <toolitem action='RectUnitsAction' />"
338         "    <separator />"
339         "    <toolitem action='RectResetAction' />"
340         "  </toolbar>"
342         "  <toolbar name='3DBoxToolbar'>"
343         "    <toolitem action='3DBoxAngleXAction' />"
344         "    <toolitem action='3DBoxVPXStateAction' />"
345         "    <separator />"
346         "    <toolitem action='3DBoxAngleYAction' />"
347         "    <toolitem action='3DBoxVPYStateAction' />"
348         "    <separator />"
349         "    <toolitem action='3DBoxAngleZAction' />"
350         "    <toolitem action='3DBoxVPZStateAction' />"
351         "  </toolbar>"
353         "  <toolbar name='SpiralToolbar'>"
354         "    <toolitem action='SpiralStateAction' />"
355         "    <toolitem action='SpiralRevolutionAction' />"
356         "    <toolitem action='SpiralExpansionAction' />"
357         "    <toolitem action='SpiralT0Action' />"
358         "    <separator />"
359         "    <toolitem action='SpiralResetAction' />"
360         "  </toolbar>"
362         "  <toolbar name='PenToolbar'>"
363         "  </toolbar>"
365         "  <toolbar name='PencilToolbar'>"
366         "  </toolbar>"
368         "  <toolbar name='CalligraphyToolbar'>"
369         "    <separator />"
370         "    <toolitem action='SetProfileAction'/>"
371         "    <toolitem action='SaveDeleteProfileAction'/>"
372         "    <separator />"
373         "    <toolitem action='CalligraphyWidthAction' />"
374         "    <toolitem action='PressureAction' />"
375         "    <toolitem action='TraceAction' />"
376         "    <toolitem action='ThinningAction' />"
377         "    <separator />"
378         "    <toolitem action='AngleAction' />"
379         "    <toolitem action='TiltAction' />"
380         "    <toolitem action='FixationAction' />"
381         "    <separator />"
382         "    <toolitem action='CapRoundingAction' />"
383         "    <separator />"
384         "    <toolitem action='TremorAction' />"
385         "    <toolitem action='WiggleAction' />"
386         "    <toolitem action='MassAction' />"
387         "    <separator />"
388         "  </toolbar>"
390         "  <toolbar name='ArcToolbar'>"
391         "    <toolitem action='ArcStateAction' />"
392         "    <separator />"
393         "    <toolitem action='ArcStartAction' />"
394         "    <toolitem action='ArcEndAction' />"
395         "    <separator />"
396         "    <toolitem action='ArcOpenAction' />"
397         "    <separator />"
398         "    <toolitem action='ArcResetAction' />"
399         "    <separator />"
400         "  </toolbar>"
402         "  <toolbar name='PaintbucketToolbar'>"
403         "    <toolitem action='ChannelsAction' />"
404         "    <separator />"
405         "    <toolitem action='ThresholdAction' />"
406         "    <separator />"
407         "    <toolitem action='OffsetAction' />"
408         "    <toolitem action='PaintbucketUnitsAction' />"
409         "    <separator />"
410         "    <toolitem action='AutoGapAction' />"
411         "    <separator />"
412         "    <toolitem action='PaintbucketResetAction' />"
413         "  </toolbar>"
415         "  <toolbar name='EraserToolbar'>"
416         "    <toolitem action='EraserWidthAction' />"
417         "    <separator />"
418         "    <toolitem action='EraserModeAction' />"
419         "  </toolbar>"
421         "  <toolbar name='DropperToolbar'>"
422         "    <toolitem action='DropperOpacityAction' />"
423         "    <toolitem action='DropperPickAlphaAction' />"
424         "    <toolitem action='DropperSetAlphaAction' />"
425         "  </toolbar>"
427         "  <toolbar name='ConnectorToolbar'>"
428         "    <toolitem action='ConnectorAvoidAction' />"
429         "    <toolitem action='ConnectorIgnoreAction' />"
430         "    <toolitem action='ConnectorSpacingAction' />"
431         "    <toolitem action='ConnectorGraphAction' />"
432         "    <toolitem action='ConnectorLengthAction' />"
433         "    <toolitem action='ConnectorDirectedAction' />"
434         "    <toolitem action='ConnectorOverlapAction' />"
435         "  </toolbar>"
437         "</ui>"
440 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
442 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
444 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
445 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
447 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
448 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
450 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
451 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
453 /* Global text entry widgets necessary for update */
454 /* GtkWidget *dropper_rgb_entry,
455           *dropper_opacity_entry ; */
456 // should be made a private member once this is converted to class
458 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
459     connection->disconnect();
460     delete connection;
463 static void purge_repr_listener( GObject* obj, GObject* tbl )
465     (void)obj;
466     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
467     if (oldrepr) { // remove old listener
468         sp_repr_remove_listener_by_data(oldrepr, tbl);
469         Inkscape::GC::release(oldrepr);
470         oldrepr = 0;
471         g_object_set_data( tbl, "repr", NULL );
472     }
475 GtkWidget *
476 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
477                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
478                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
480     SPAction *action = verb->get_action(view);
481     if (!action) return NULL;
483     SPAction *doubleclick_action;
484     if (doubleclick_verb)
485         doubleclick_action = doubleclick_verb->get_action(view);
486     else
487         doubleclick_action = NULL;
489     /* fixme: Handle sensitive/unsensitive */
490     /* fixme: Implement sp_button_new_from_action */
491     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
492     gtk_widget_show(b);
493     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
495     return b;
498 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
499                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
501     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
504 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
505                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
507     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
511 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
513     SPAction* targetAction = SP_ACTION(user_data);
514     if ( targetAction ) {
515         sp_action_perform( targetAction, NULL );
516     }
519 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
521     if ( data ) {
522         GtkAction* act = GTK_ACTION(data);
523         gtk_action_set_sensitive( act, sensitive );
524     }
527 static SPActionEventVector action_event_vector = {
528     {NULL},
529     NULL,
530     NULL,
531     sp_action_action_set_sensitive,
532     NULL,
533     NULL
534 };
536 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
538     GtkAction* act = 0;
540     SPAction* targetAction = verb->get_action(view);
541     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
542     act = GTK_ACTION(inky);
543     gtk_action_set_sensitive( act, targetAction->sensitive );
545     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
547     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
548     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
550     return act;
553 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
555     Inkscape::UI::View::View *view = desktop;
556     gint verbsToUse[] = {
557         // disabled until we have icons for them:
558         //find
559         //SP_VERB_EDIT_TILE,
560         //SP_VERB_EDIT_UNTILE,
561         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
562         SP_VERB_DIALOG_DISPLAY,
563         SP_VERB_DIALOG_FILL_STROKE,
564         SP_VERB_DIALOG_NAMEDVIEW,
565         SP_VERB_DIALOG_TEXT,
566         SP_VERB_DIALOG_XML_EDITOR,
567         SP_VERB_EDIT_CLONE,
568         SP_VERB_EDIT_COPY,
569         SP_VERB_EDIT_CUT,
570         SP_VERB_EDIT_DUPLICATE,
571         SP_VERB_EDIT_PASTE,
572         SP_VERB_EDIT_REDO,
573         SP_VERB_EDIT_UNDO,
574         SP_VERB_EDIT_UNLINK_CLONE,
575         SP_VERB_FILE_EXPORT,
576         SP_VERB_FILE_IMPORT,
577         SP_VERB_FILE_NEW,
578         SP_VERB_FILE_OPEN,
579         SP_VERB_FILE_PRINT,
580         SP_VERB_FILE_SAVE,
581         SP_VERB_OBJECT_TO_CURVE,
582         SP_VERB_SELECTION_GROUP,
583         SP_VERB_SELECTION_OUTLINE,
584         SP_VERB_SELECTION_UNGROUP,
585         SP_VERB_ZOOM_1_1,
586         SP_VERB_ZOOM_1_2,
587         SP_VERB_ZOOM_2_1,
588         SP_VERB_ZOOM_DRAWING,
589         SP_VERB_ZOOM_IN,
590         SP_VERB_ZOOM_NEXT,
591         SP_VERB_ZOOM_OUT,
592         SP_VERB_ZOOM_PAGE,
593         SP_VERB_ZOOM_PAGE_WIDTH,
594         SP_VERB_ZOOM_PREV,
595         SP_VERB_ZOOM_SELECTION,
596     };
598     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
600     static std::map<SPDesktop*, GtkActionGroup*> groups;
601     GtkActionGroup* mainActions = 0;
602     if ( groups.find(desktop) != groups.end() ) {
603         mainActions = groups[desktop];
604     }
606     if ( !mainActions ) {
607         mainActions = gtk_action_group_new("main");
608         groups[desktop] = mainActions;
609     }
611     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
612         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
613         if ( verb ) {
614             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
615                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
616                 gtk_action_group_add_action( mainActions, act );
617             }
618         }
619     }
621     return mainActions;
625 GtkWidget *
626 sp_tool_toolbox_new()
628     GtkTooltips *tt = gtk_tooltips_new();
629     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
631     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
632     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
634     gtk_widget_set_sensitive(tb, FALSE);
636     GtkWidget *hb = gtk_handle_box_new();
637     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
638     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
639     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
641     gtk_container_add(GTK_CONTAINER(hb), tb);
642     gtk_widget_show(GTK_WIDGET(tb));
644     sigc::connection* conn = new sigc::connection;
645     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
647     return hb;
650 static void
651 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
653     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
654     gtk_widget_queue_resize(child);
657 static void
658 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
660     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
661     gtk_widget_queue_resize(child);
664 GtkWidget *
665 sp_aux_toolbox_new()
667     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
669     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
671     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
673     gtk_widget_set_sensitive(tb, FALSE);
675     GtkWidget *hb = gtk_handle_box_new();
676     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
677     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
678     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
680     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
681     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
683     gtk_container_add(GTK_CONTAINER(hb), tb);
684     gtk_widget_show(GTK_WIDGET(tb));
686     sigc::connection* conn = new sigc::connection;
687     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
689     return hb;
692 //####################################
693 //# Commands Bar
694 //####################################
696 GtkWidget *
697 sp_commands_toolbox_new()
699     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
701     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
703     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
704     gtk_widget_set_sensitive(tb, FALSE);
706     GtkWidget *hb = gtk_handle_box_new();
707     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
708     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
709     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
711     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
712     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
714     gtk_container_add(GTK_CONTAINER(hb), tb);
715     gtk_widget_show(GTK_WIDGET(tb));
717     sigc::connection* conn = new sigc::connection;
718     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
720     return hb;
723 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
724                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
725                                                        gchar const *path, gchar const *data, gdouble def,
726                                                        GtkWidget *focusTarget,
727                                                        GtkWidget *us,
728                                                        GObject *dataKludge,
729                                                        gboolean altx, gchar const *altx_mark,
730                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
731                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
732                                                        void (*callback)(GtkAdjustment *, GObject *),
733                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
735     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
736                                                              lower, upper, step, page, page ) );
737     if (us) {
738         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
739     }
741     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
743     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
744     if ( shortLabel ) {
745         g_object_set( act, "short_label", shortLabel, NULL );
746     }
748     if ( (descrCount > 0) && descrLabels && descrValues ) {
749         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
750     }
752     if ( focusTarget ) {
753         ege_adjustment_action_set_focuswidget( act, focusTarget );
754     }
756     if ( altx && altx_mark ) {
757         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
758     }
760     if ( dataKludge ) {
761         g_object_set_data( dataKludge, data, adj );
762     }
764     // Using a cast just to make sure we pass in the right kind of function pointer
765     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
767     return act;
771 //####################################
772 //# node editing callbacks
773 //####################################
775 /**
776  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
777  */
778 static ShapeEditor *get_current_shape_editor()
780     if (!SP_ACTIVE_DESKTOP) {
781         return NULL;
782     }
784     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
786     if (!SP_IS_NODE_CONTEXT(event_context)) {
787         return NULL;
788     }
790     return SP_NODE_CONTEXT(event_context)->shape_editor;
794 void
795 sp_node_path_edit_add(void)
797     ShapeEditor *shape_editor = get_current_shape_editor();
798     if (shape_editor) shape_editor->add_node();
801 void
802 sp_node_path_edit_delete(void)
804     ShapeEditor *shape_editor = get_current_shape_editor();
805     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
808 void
809 sp_node_path_edit_delete_segment(void)
811     ShapeEditor *shape_editor = get_current_shape_editor();
812     if (shape_editor) shape_editor->delete_segment();
815 void
816 sp_node_path_edit_break(void)
818     ShapeEditor *shape_editor = get_current_shape_editor();
819     if (shape_editor) shape_editor->break_at_nodes();
822 void
823 sp_node_path_edit_join(void)
825     ShapeEditor *shape_editor = get_current_shape_editor();
826     if (shape_editor) shape_editor->join_nodes();
829 void
830 sp_node_path_edit_join_segment(void)
832     ShapeEditor *shape_editor = get_current_shape_editor();
833     if (shape_editor) shape_editor->join_segments();
836 void
837 sp_node_path_edit_toline(void)
839     ShapeEditor *shape_editor = get_current_shape_editor();
840     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
843 void
844 sp_node_path_edit_tocurve(void)
846     ShapeEditor *shape_editor = get_current_shape_editor();
847     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
850 void
851 sp_node_path_edit_cusp(void)
853     ShapeEditor *shape_editor = get_current_shape_editor();
854     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
857 void
858 sp_node_path_edit_smooth(void)
860     ShapeEditor *shape_editor = get_current_shape_editor();
861     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
864 void
865 sp_node_path_edit_symmetrical(void)
867     ShapeEditor *shape_editor = get_current_shape_editor();
868     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
871 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
872     bool show = gtk_toggle_action_get_active( act );
873     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
874     ShapeEditor *shape_editor = get_current_shape_editor();
875     if (shape_editor) shape_editor->show_handles(show);
878 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
879     bool show = gtk_toggle_action_get_active( act );
880     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
881     ShapeEditor *shape_editor = get_current_shape_editor();
882     if (shape_editor) shape_editor->show_helperpath(show);
885 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
886     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
889 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
890     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
893 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
894     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
897 /* is called when the node selection is modified */
898 static void
899 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
901     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
902     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
903     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
904     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
906     // quit if run by the attr_changed listener
907     if (g_object_get_data( tbl, "freeze" )) {
908         return;
909     }
911     // in turn, prevent listener from responding
912     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
914     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
915     SPUnit const *unit = tracker->getActiveUnit();
917     ShapeEditor *shape_editor = get_current_shape_editor();
918     if (shape_editor && shape_editor->has_nodepath()) {
919         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
920         int n_selected = 0;
921         if (nodepath) {
922             n_selected = nodepath->numSelected();
923         }
925         if (n_selected == 0) {
926             gtk_action_set_sensitive(xact, FALSE);
927             gtk_action_set_sensitive(yact, FALSE);
928         } else {
929             gtk_action_set_sensitive(xact, TRUE);
930             gtk_action_set_sensitive(yact, TRUE);
931             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
932             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
934             if (n_selected == 1) {
935                 NR::Point sel_node = nodepath->singleSelectedCoords();
936                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
937                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
938                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
939                 }
940             } else {
941                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
942                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
943                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
944                     /* Note: Currently x and y will always have a value, even if the coordinates of the
945                        selected nodes don't coincide (in this case we use the coordinates of the center
946                        of the bounding box). So the entries are never set to zero. */
947                     // FIXME: Maybe we should clear the entry if several nodes are selected
948                     //        instead of providing a kind of average value
949                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
950                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
951                 }
952             }
953         }
954     } else {
955         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
956         gtk_action_set_sensitive(xact, FALSE);
957         gtk_action_set_sensitive(yact, FALSE);
958     }
960     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
963 static void
964 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
966     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
968     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
969     SPUnit const *unit = tracker->getActiveUnit();
971     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
972         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
973     }
975     // quit if run by the attr_changed listener
976     if (g_object_get_data( tbl, "freeze" )) {
977         return;
978     }
980     // in turn, prevent listener from responding
981     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
983     ShapeEditor *shape_editor = get_current_shape_editor();
984     if (shape_editor && shape_editor->has_nodepath()) {
985         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
986         if (!strcmp(value_name, "x")) {
987             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
988         }
989         if (!strcmp(value_name, "y")) {
990             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
991         }
992     }
994     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
997 static void
998 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1000     sp_node_path_value_changed(adj, tbl, "x");
1003 static void
1004 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1006     sp_node_path_value_changed(adj, tbl, "y");
1009 void
1010 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1012     {
1013     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1014     SPItem *item = selection->singleItem();
1015     if (item && SP_IS_LPE_ITEM(item)) {
1016        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1017            gtk_action_set_sensitive(w, TRUE);
1018        } else {
1019            gtk_action_set_sensitive(w, FALSE);
1020        }
1021     } else {
1022        gtk_action_set_sensitive(w, FALSE);
1023     }
1024     }
1026     {
1027     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1028     SPItem *item = selection->singleItem();
1029     if (item && item->clip_ref && item->clip_ref->getObject()) {
1030        gtk_action_set_sensitive(w, TRUE);
1031     } else {
1032        gtk_action_set_sensitive(w, FALSE);
1033     }
1034     }
1036     {
1037     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1038     SPItem *item = selection->singleItem();
1039     if (item && item->mask_ref && item->mask_ref->getObject()) {
1040        gtk_action_set_sensitive(w, TRUE);
1041     } else {
1042        gtk_action_set_sensitive(w, FALSE);
1043     }
1044     }
1047 void
1048 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1050     sp_node_toolbox_sel_changed (selection, tbl);
1055 //################################
1056 //##    Node Editing Toolbox    ##
1057 //################################
1059 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1061     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1062     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1063     g_object_set_data( holder, "tracker", tracker );
1065     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1067     {
1068         InkAction* inky = ink_action_new( "NodeInsertAction",
1069                                           _("Insert node"),
1070                                           _("Insert new nodes into selected segments"),
1071                                           "node_insert",
1072                                           secondarySize );
1073         g_object_set( inky, "short_label", _("Insert"), NULL );
1074         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1075         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1076     }
1078     {
1079         InkAction* inky = ink_action_new( "NodeDeleteAction",
1080                                           _("Delete node"),
1081                                           _("Delete selected nodes"),
1082                                           "node_delete",
1083                                           secondarySize );
1084         g_object_set( inky, "short_label", _("Delete"), NULL );
1085         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1086         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1087     }
1089     {
1090         InkAction* inky = ink_action_new( "NodeJoinAction",
1091                                           _("Join endnodes"),
1092                                           _("Join selected endnodes"),
1093                                           "node_join",
1094                                           secondarySize );
1095         g_object_set( inky, "short_label", _("Join"), NULL );
1096         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1097         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1098     }
1100     {
1101         InkAction* inky = ink_action_new( "NodeBreakAction",
1102                                           _("Break nodes"),
1103                                           _("Break path at selected nodes"),
1104                                           "node_break",
1105                                           secondarySize );
1106         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1107         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1108     }
1111     {
1112         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1113                                           _("Join with segment"),
1114                                           _("Join selected endnodes with a new segment"),
1115                                           "node_join_segment",
1116                                           secondarySize );
1117         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1118         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1119     }
1121     {
1122         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1123                                           _("Delete segment"),
1124                                           _("Delete segment between two non-endpoint nodes"),
1125                                           "node_delete_segment",
1126                                           secondarySize );
1127         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1128         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1129     }
1131     {
1132         InkAction* inky = ink_action_new( "NodeCuspAction",
1133                                           _("Node Cusp"),
1134                                           _("Make selected nodes corner"),
1135                                           "node_cusp",
1136                                           secondarySize );
1137         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1138         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1139     }
1141     {
1142         InkAction* inky = ink_action_new( "NodeSmoothAction",
1143                                           _("Node Smooth"),
1144                                           _("Make selected nodes smooth"),
1145                                           "node_smooth",
1146                                           secondarySize );
1147         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1148         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1149     }
1151     {
1152         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1153                                           _("Node Symmetric"),
1154                                           _("Make selected nodes symmetric"),
1155                                           "node_symmetric",
1156                                           secondarySize );
1157         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1158         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1159     }
1161     {
1162         InkAction* inky = ink_action_new( "NodeLineAction",
1163                                           _("Node Line"),
1164                                           _("Make selected segments lines"),
1165                                           "node_line",
1166                                           secondarySize );
1167         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1168         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1169     }
1171     {
1172         InkAction* inky = ink_action_new( "NodeCurveAction",
1173                                           _("Node Curve"),
1174                                           _("Make selected segments curves"),
1175                                           "node_curve",
1176                                           secondarySize );
1177         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1178         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1179     }
1181     {
1182         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1183                                                       _("Show Handles"),
1184                                                       _("Show the Bezier handles of selected nodes"),
1185                                                       "nodes_show_handles",
1186                                                       Inkscape::ICON_SIZE_DECORATION );
1187         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1188         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1189         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1190     }
1192     {
1193         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1194                                                       _("Show Outline"),
1195                                                       _("Show the outline of the path"),
1196                                                       "nodes_show_helperpath",
1197                                                       Inkscape::ICON_SIZE_DECORATION );
1198         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1199         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1200         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1201     }
1203     {
1204         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1205                                           _("Next path effect parameter"),
1206                                           _("Show next path effect parameter for editing"),
1207                                           "edit_next_parameter",
1208                                           Inkscape::ICON_SIZE_DECORATION );
1209         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1210         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1211         g_object_set_data( holder, "nodes_lpeedit", inky);
1212     }
1214     {
1215         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1216                                           _("Edit clipping path"),
1217                                           _("Edit the clipping path of the object"),
1218                                           "nodeedit-clippath",
1219                                           Inkscape::ICON_SIZE_DECORATION );
1220         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1221         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1222         g_object_set_data( holder, "nodes_clippathedit", inky);
1223     }
1225     {
1226         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1227                                           _("Edit mask path"),
1228                                           _("Edit the mask of the object"),
1229                                           "nodeedit-mask",
1230                                           Inkscape::ICON_SIZE_DECORATION );
1231         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1232         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1233         g_object_set_data( holder, "nodes_maskedit", inky);
1234     }
1236     /* X coord of selected node(s) */
1237     {
1238         EgeAdjustmentAction* eact = 0;
1239         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1240         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1241         eact = create_adjustment_action( "NodeXAction",
1242                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1243                                          "tools.nodes", "Xcoord", 0,
1244                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1245                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1246                                          labels, values, G_N_ELEMENTS(labels),
1247                                          sp_node_path_x_value_changed );
1248         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1249         g_object_set_data( holder, "nodes_x_action", eact );
1250         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1251         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1252     }
1254     /* Y coord of selected node(s) */
1255     {
1256         EgeAdjustmentAction* eact = 0;
1257         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1258         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1259         eact = create_adjustment_action( "NodeYAction",
1260                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1261                                          "tools.nodes", "Ycoord", 0,
1262                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1263                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1264                                          labels, values, G_N_ELEMENTS(labels),
1265                                          sp_node_path_y_value_changed );
1266         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1267         g_object_set_data( holder, "nodes_y_action", eact );
1268         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1269         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1270     }
1272     // add the units menu
1273     {
1274         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1275         gtk_action_group_add_action( mainActions, act );
1276     }
1279     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1281     //watch selection
1282     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1284     sigc::connection *c_selection_changed =
1285         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1286                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1287     pool->add_connection ("selection-changed", c_selection_changed);
1289     sigc::connection *c_selection_modified =
1290         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1291                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1292     pool->add_connection ("selection-modified", c_selection_modified);
1294     sigc::connection *c_subselection_changed =
1295         new sigc::connection (desktop->connectToolSubselectionChanged
1296                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1297     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1299     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1301     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1302 } // end of sp_node_toolbox_prep()
1305 //########################
1306 //##    Zoom Toolbox    ##
1307 //########################
1309 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1311     // no custom GtkAction setup needed
1312 } // end of sp_zoom_toolbox_prep()
1314 void
1315 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1317     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")));
1321 void
1322 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1324     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")));
1327 void
1328 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1330     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")));
1333 static void
1334 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1336     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1337     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1339     if (old_desktop) {
1340         GList *children, *iter;
1342         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1343         for ( iter = children ; iter ; iter = iter->next ) {
1344             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1345         }
1346         g_list_free(children);
1347     }
1349     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1351     if (desktop) {
1352         gtk_widget_set_sensitive(toolbox, TRUE);
1353         setup_func(toolbox, desktop);
1354         update_func(desktop, desktop->event_context, toolbox);
1355         *conn = desktop->connectEventContextChanged
1356             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1357     } else {
1358         gtk_widget_set_sensitive(toolbox, FALSE);
1359     }
1361 } // end of toolbox_set_desktop()
1364 static void
1365 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1367     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1368     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1370     for (int i = 0 ; tools[i].type_name ; i++ ) {
1371         GtkWidget *button =
1372             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1373                                                               SP_BUTTON_TYPE_TOGGLE,
1374                                                               Inkscape::Verb::get(tools[i].verb),
1375                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1376                                                               desktop,
1377                                                               tooltips );
1379         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1380                            (gpointer)button );
1381     }
1385 static void
1386 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1388     gchar const *const tname = ( eventcontext
1389                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1390                                  : NULL );
1391     for (int i = 0 ; tools[i].type_name ; i++ ) {
1392         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1393         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1394     }
1397 static void
1398 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1400     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1401     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1402     GtkUIManager* mgr = gtk_ui_manager_new();
1403     GError* errVal = 0;
1404     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1405     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1407     std::map<std::string, GtkWidget*> dataHolders;
1409     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1410         if ( aux_toolboxes[i].prep_func ) {
1411             // converted to GtkActions and UIManager
1413             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1414             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1415             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1416             dataHolders[aux_toolboxes[i].type_name] = kludge;
1417             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1418         } else {
1420             GtkWidget *sub_toolbox = 0;
1421             if (aux_toolboxes[i].create_func == NULL)
1422                 sub_toolbox = sp_empty_toolbox_new(desktop);
1423             else {
1424                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1425             }
1427             gtk_size_group_add_widget( grouper, sub_toolbox );
1429             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1430             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1432         }
1433     }
1435     // Second pass to create toolbars *after* all GtkActions are created
1436     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1437         if ( aux_toolboxes[i].prep_func ) {
1438             // converted to GtkActions and UIManager
1440             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1442             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1443             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1445             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1446             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1447             g_free( tmp );
1448             tmp = 0;
1450             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1451             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1452                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1453             }
1454             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1457             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1459             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1460                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1461                 swatch->setDesktop( desktop );
1462                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1463                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1464                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1465                 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 );
1466             }
1468             gtk_widget_show_all( holder );
1469             sp_set_font_size_smaller( holder );
1471             gtk_size_group_add_widget( grouper, holder );
1473             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1474             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1475         }
1476     }
1478     g_object_unref( G_OBJECT(grouper) );
1481 static void
1482 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1484     gchar const *tname = ( eventcontext
1485                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1486                            : NULL );
1487     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1488         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1489         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1490             gtk_widget_show_all(sub_toolbox);
1491             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1492         } else {
1493             gtk_widget_hide(sub_toolbox);
1494         }
1495     }
1498 static void
1499 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1501     gchar const * descr =
1502         "<ui>"
1503         "  <toolbar name='CommandsToolbar'>"
1504         "    <toolitem action='FileNew' />"
1505         "    <toolitem action='FileOpen' />"
1506         "    <toolitem action='FileSave' />"
1507         "    <toolitem action='FilePrint' />"
1508         "    <separator />"
1509         "    <toolitem action='FileImport' />"
1510         "    <toolitem action='FileExport' />"
1511         "    <separator />"
1512         "    <toolitem action='EditUndo' />"
1513         "    <toolitem action='EditRedo' />"
1514         "    <separator />"
1515         "    <toolitem action='EditCopy' />"
1516         "    <toolitem action='EditCut' />"
1517         "    <toolitem action='EditPaste' />"
1518         "    <separator />"
1519         "    <toolitem action='ZoomSelection' />"
1520         "    <toolitem action='ZoomDrawing' />"
1521         "    <toolitem action='ZoomPage' />"
1522         "    <separator />"
1523         "    <toolitem action='EditDuplicate' />"
1524         "    <toolitem action='EditClone' />"
1525         "    <toolitem action='EditUnlinkClone' />"
1526         "    <separator />"
1527         "    <toolitem action='SelectionGroup' />"
1528         "    <toolitem action='SelectionUnGroup' />"
1529         "    <separator />"
1530         "    <toolitem action='DialogFillStroke' />"
1531         "    <toolitem action='DialogText' />"
1532         "    <toolitem action='DialogXMLEditor' />"
1533         "    <toolitem action='DialogAlignDistribute' />"
1534         "    <separator />"
1535         "    <toolitem action='DialogPreferences' />"
1536         "    <toolitem action='DialogDocumentProperties' />"
1537         "  </toolbar>"
1538         "</ui>";
1539     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1542     GtkUIManager* mgr = gtk_ui_manager_new();
1543     GError* errVal = 0;
1545     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1546     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1548     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1549     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1550         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1551     }
1553     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1554     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1557     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1560 static void
1561 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1565 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1567     gtk_widget_show(toolbox_toplevel);
1568     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1570     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1571     if (!shown_toolbox) {
1572         return;
1573     }
1574     gtk_widget_show(toolbox);
1576     gtk_widget_show_all(shown_toolbox);
1579 void
1580 aux_toolbox_space(GtkWidget *tb, gint space)
1582     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1585 static GtkWidget *
1586 sp_empty_toolbox_new(SPDesktop *desktop)
1588     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1589     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1590     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1592     gtk_widget_show_all(tbl);
1593     sp_set_font_size_smaller (tbl);
1595     return tbl;
1598 // helper UI functions
1600 GtkWidget *
1601 sp_tb_spinbutton(
1602     gchar *label, gchar const *tooltip,
1603     gchar const *path, gchar const *data, gdouble def,
1604     GtkWidget *us,
1605     GtkWidget *tbl,
1606     gboolean altx, gchar const *altx_mark,
1607     gdouble lower, gdouble upper, gdouble step, gdouble page,
1608     void (*callback)(GtkAdjustment *, GtkWidget *),
1609     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1611     GtkTooltips *tt = gtk_tooltips_new();
1613     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1615     GtkWidget *l = gtk_label_new(label);
1616     gtk_widget_show(l);
1617     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1618     gtk_container_add(GTK_CONTAINER(hb), l);
1620     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1621                                       lower, upper, step, page, page);
1622     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1623     if (us)
1624         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1626     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1627     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1628     if (altx)
1629         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1630     gtk_widget_set_size_request(sb,
1631                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1632                                 AUX_SPINBUTTON_HEIGHT);
1633     gtk_widget_show(sb);
1634     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1635     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1636     gtk_container_add(GTK_CONTAINER(hb), sb);
1637     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1639     return hb;
1642 #define MODE_LABEL_WIDTH 70
1644 //########################
1645 //##       Star         ##
1646 //########################
1648 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1650     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1652     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1653         // do not remember prefs if this call is initiated by an undo change, because undoing object
1654         // creation sets bogus values to its attributes before it is deleted
1655         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1656     }
1658     // quit if run by the attr_changed listener
1659     if (g_object_get_data( dataKludge, "freeze" )) {
1660         return;
1661     }
1663     // in turn, prevent listener from responding
1664     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1666     bool modmade = false;
1668     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1669     GSList const *items = selection->itemList();
1670     for (; items != NULL; items = items->next) {
1671         if (SP_IS_STAR((SPItem *) items->data)) {
1672             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1673             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1674             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1675                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1676                                     + M_PI / (gint)adj->value));
1677             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1678             modmade = true;
1679         }
1680     }
1681     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1682                                    _("Star: Change number of corners"));
1684     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1687 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1689     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1691     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1692         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1693     }
1695     // quit if run by the attr_changed listener
1696     if (g_object_get_data( dataKludge, "freeze" )) {
1697         return;
1698     }
1700     // in turn, prevent listener from responding
1701     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1703     bool modmade = false;
1704     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1705     GSList const *items = selection->itemList();
1706     for (; items != NULL; items = items->next) {
1707         if (SP_IS_STAR((SPItem *) items->data)) {
1708             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1710             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1711             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1712             if (r2 < r1) {
1713                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1714             } else {
1715                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1716             }
1718             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1719             modmade = true;
1720         }
1721     }
1723     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1724                                    _("Star: Change spoke ratio"));
1726     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1729 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1731     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1732     bool flat = ege_select_one_action_get_active( act ) == 0;
1734     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1735         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1736                                     flat ? "true" : "false" );
1737     }
1739     // quit if run by the attr_changed listener
1740     if (g_object_get_data( dataKludge, "freeze" )) {
1741         return;
1742     }
1744     // in turn, prevent listener from responding
1745     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1747     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1748     GSList const *items = selection->itemList();
1749     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1750     bool modmade = false;
1752     if ( prop_action ) {
1753         gtk_action_set_sensitive( prop_action, !flat );
1754     }
1756     for (; items != NULL; items = items->next) {
1757         if (SP_IS_STAR((SPItem *) items->data)) {
1758             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1759             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1760             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1761             modmade = true;
1762         }
1763     }
1765     if (modmade) {
1766         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1767                          flat ? _("Make polygon") : _("Make star"));
1768     }
1770     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1773 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1775     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1777     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1778         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1779     }
1781     // quit if run by the attr_changed listener
1782     if (g_object_get_data( dataKludge, "freeze" )) {
1783         return;
1784     }
1786     // in turn, prevent listener from responding
1787     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1789     bool modmade = false;
1791     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1792     GSList const *items = selection->itemList();
1793     for (; items != NULL; items = items->next) {
1794         if (SP_IS_STAR((SPItem *) items->data)) {
1795             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1796             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1797             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1798             modmade = true;
1799         }
1800     }
1801     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1802                                    _("Star: Change rounding"));
1804     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1807 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1809     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1811     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1812         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1813     }
1815     // quit if run by the attr_changed listener
1816     if (g_object_get_data( dataKludge, "freeze" )) {
1817         return;
1818     }
1820     // in turn, prevent listener from responding
1821     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1823     bool modmade = false;
1825     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1826     GSList const *items = selection->itemList();
1827     for (; items != NULL; items = items->next) {
1828         if (SP_IS_STAR((SPItem *) items->data)) {
1829             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1830             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1831             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1832             modmade = true;
1833         }
1834     }
1835     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1836                                    _("Star: Change randomization"));
1838     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1842 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1843                                        gchar const */*old_value*/, gchar const */*new_value*/,
1844                                        bool /*is_interactive*/, gpointer data)
1846     GtkWidget *tbl = GTK_WIDGET(data);
1848     // quit if run by the _changed callbacks
1849     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1850         return;
1851     }
1853     // in turn, prevent callbacks from responding
1854     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1856     GtkAdjustment *adj = 0;
1858     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1859     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1861     if (!strcmp(name, "inkscape:randomized")) {
1862         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1863         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1864     } else if (!strcmp(name, "inkscape:rounded")) {
1865         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1866         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1867     } else if (!strcmp(name, "inkscape:flatsided")) {
1868         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1869         char const *flatsides = repr->attribute("inkscape:flatsided");
1870         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1871         if ( flatsides && !strcmp(flatsides,"false") ) {
1872             ege_select_one_action_set_active( flat_action, 1 );
1873             gtk_action_set_sensitive( prop_action, TRUE );
1874         } else {
1875             ege_select_one_action_set_active( flat_action, 0 );
1876             gtk_action_set_sensitive( prop_action, FALSE );
1877         }
1878     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1879         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1880         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1881         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1882         if (r2 < r1) {
1883             gtk_adjustment_set_value(adj, r2/r1);
1884         } else {
1885             gtk_adjustment_set_value(adj, r1/r2);
1886         }
1887     } else if (!strcmp(name, "sodipodi:sides")) {
1888         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1889         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1890     }
1892     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1896 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1898     NULL, /* child_added */
1899     NULL, /* child_removed */
1900     star_tb_event_attr_changed,
1901     NULL, /* content_changed */
1902     NULL  /* order_changed */
1903 };
1906 /**
1907  *  \param selection Should not be NULL.
1908  */
1909 static void
1910 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1912     int n_selected = 0;
1913     Inkscape::XML::Node *repr = NULL;
1915     purge_repr_listener( tbl, tbl );
1917     for (GSList const *items = selection->itemList();
1918          items != NULL;
1919          items = items->next)
1920     {
1921         if (SP_IS_STAR((SPItem *) items->data)) {
1922             n_selected++;
1923             repr = SP_OBJECT_REPR((SPItem *) items->data);
1924         }
1925     }
1927     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1929     if (n_selected == 0) {
1930         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1931     } else if (n_selected == 1) {
1932         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1934         if (repr) {
1935             g_object_set_data( tbl, "repr", repr );
1936             Inkscape::GC::anchor(repr);
1937             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1938             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1939         }
1940     } else {
1941         // FIXME: implement averaging of all parameters for multiple selected stars
1942         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1943         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1944     }
1948 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1950     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1951     // callbacks to lump all the changes for all selected objects in one undo step
1953     GtkAdjustment *adj = 0;
1955     // fixme: make settable in prefs!
1956     gint mag = 5;
1957     gdouble prop = 0.5;
1958     gboolean flat = FALSE;
1959     gdouble randomized = 0;
1960     gdouble rounded = 0;
1962     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1963     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1965     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1966     gtk_action_set_sensitive( sb2, !flat );
1968     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1969     gtk_adjustment_set_value(adj, mag);
1970     gtk_adjustment_value_changed(adj);
1972     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1973     gtk_adjustment_set_value(adj, prop);
1974     gtk_adjustment_value_changed(adj);
1976     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1977     gtk_adjustment_set_value(adj, rounded);
1978     gtk_adjustment_value_changed(adj);
1980     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1981     gtk_adjustment_set_value(adj, randomized);
1982     gtk_adjustment_value_changed(adj);
1986 void
1987 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1989     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1990     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1991     GtkWidget *l = gtk_label_new(NULL);
1992     gtk_label_set_markup(GTK_LABEL(l), title);
1993     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1994     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1995     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1999 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2001     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2003     {
2004         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2005         ege_output_action_set_use_markup( act, TRUE );
2006         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2007         g_object_set_data( holder, "mode_action", act );
2008     }
2010     {
2011         EgeAdjustmentAction* eact = 0;
2012         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2013         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2015         /* Flatsided checkbox */
2016         {
2017             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2019             GtkTreeIter iter;
2020             gtk_list_store_append( model, &iter );
2021             gtk_list_store_set( model, &iter,
2022                                 0, _("Polygon"),
2023                                 1, _("Regular polygon (with one handle) instead of a star"),
2024                                 2, "star_flat",
2025                                 -1 );
2027             gtk_list_store_append( model, &iter );
2028             gtk_list_store_set( model, &iter,
2029                                 0, _("Star"),
2030                                 1, _("Star instead of a regular polygon (with one handle)"),
2031                                 2, "star_angled",
2032                                 -1 );
2034             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2035             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2036             g_object_set_data( holder, "flat_action", act );
2038             ege_select_one_action_set_appearance( act, "full" );
2039             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2040             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2041             ege_select_one_action_set_icon_column( act, 2 );
2042             ege_select_one_action_set_icon_size( act, secondarySize );
2043             ege_select_one_action_set_tooltip_column( act, 1  );
2045             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2046             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2047         }
2049         /* Magnitude */
2050         {
2051         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2052         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2053         eact = create_adjustment_action( "MagnitudeAction",
2054                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2055                                          "tools.shapes.star", "magnitude", 3,
2056                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2057                                          3, 1024, 1, 5,
2058                                          labels, values, G_N_ELEMENTS(labels),
2059                                          sp_stb_magnitude_value_changed,
2060                                          1.0, 0 );
2061         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2062         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2063         }
2065         /* Spoke ratio */
2066         {
2067         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2068         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2069         eact = create_adjustment_action( "SpokeAction",
2070                                          _("Spoke ratio"), _("Spoke ratio:"),
2071                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2072                                          // Base radius is the same for the closest handle.
2073                                          _("Base radius to tip radius ratio"),
2074                                          "tools.shapes.star", "proportion", 0.5,
2075                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2076                                          0.01, 1.0, 0.01, 0.1,
2077                                          labels, values, G_N_ELEMENTS(labels),
2078                                          sp_stb_proportion_value_changed );
2079         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2080         g_object_set_data( holder, "prop_action", eact );
2081         }
2083         if ( !isFlatSided ) {
2084             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2085         } else {
2086             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2087         }
2089         /* Roundedness */
2090         {
2091         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2092         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2093         eact = create_adjustment_action( "RoundednessAction",
2094                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2095                                          "tools.shapes.star", "rounded", 0.0,
2096                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2097                                          -10.0, 10.0, 0.01, 0.1,
2098                                          labels, values, G_N_ELEMENTS(labels),
2099                                          sp_stb_rounded_value_changed );
2100         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2101         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2102         }
2104         /* Randomization */
2105         {
2106         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2107         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2108         eact = create_adjustment_action( "RandomizationAction",
2109                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2110                                          "tools.shapes.star", "randomized", 0.0,
2111                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2112                                          -10.0, 10.0, 0.001, 0.01,
2113                                          labels, values, G_N_ELEMENTS(labels),
2114                                          sp_stb_randomized_value_changed, 0.1, 3 );
2115         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2116         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2117         }
2118     }
2120     {
2121         /* Reset */
2122         {
2123             GtkAction* act = gtk_action_new( "StarResetAction",
2124                                              _("Defaults"),
2125                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2126                                              GTK_STOCK_CLEAR );
2127             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2128             gtk_action_group_add_action( mainActions, act );
2129             gtk_action_set_sensitive( act, TRUE );
2130         }
2131     }
2133     sigc::connection *connection = new sigc::connection(
2134         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2135         );
2136     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2137     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2141 //########################
2142 //##       Rect         ##
2143 //########################
2145 static void sp_rtb_sensitivize( GObject *tbl )
2147     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2148     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2149     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2151     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2152         gtk_action_set_sensitive( not_rounded, FALSE );
2153     } else {
2154         gtk_action_set_sensitive( not_rounded, TRUE );
2155     }
2159 static void
2160 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2161                           void (*setter)(SPRect *, gdouble))
2163     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2165     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2166     SPUnit const *unit = tracker->getActiveUnit();
2168     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2169         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2170     }
2172     // quit if run by the attr_changed listener
2173     if (g_object_get_data( tbl, "freeze" )) {
2174         return;
2175     }
2177     // in turn, prevent listener from responding
2178     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2180     bool modmade = false;
2181     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2182     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2183         if (SP_IS_RECT(items->data)) {
2184             if (adj->value != 0) {
2185                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2186             } else {
2187                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2188             }
2189             modmade = true;
2190         }
2191     }
2193     sp_rtb_sensitivize( tbl );
2195     if (modmade) {
2196         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2197                                    _("Change rectangle"));
2198     }
2200     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2203 static void
2204 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2206     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2209 static void
2210 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2212     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2215 static void
2216 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2218     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2221 static void
2222 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2224     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2229 static void
2230 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2232     GtkAdjustment *adj = 0;
2234     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2235     gtk_adjustment_set_value(adj, 0.0);
2236     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2237     gtk_adjustment_value_changed(adj);
2239     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2240     gtk_adjustment_set_value(adj, 0.0);
2241     gtk_adjustment_value_changed(adj);
2243     sp_rtb_sensitivize( obj );
2246 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2247                                        gchar const */*old_value*/, gchar const */*new_value*/,
2248                                        bool /*is_interactive*/, gpointer data)
2250     GObject *tbl = G_OBJECT(data);
2252     // quit if run by the _changed callbacks
2253     if (g_object_get_data( tbl, "freeze" )) {
2254         return;
2255     }
2257     // in turn, prevent callbacks from responding
2258     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2260     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2261     SPUnit const *unit = tracker->getActiveUnit();
2263     gpointer item = g_object_get_data( tbl, "item" );
2264     if (item && SP_IS_RECT(item)) {
2265         {
2266             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2267             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2268             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2269         }
2271         {
2272             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2273             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2274             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2275         }
2277         {
2278             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2279             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2280             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2281         }
2283         {
2284             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2285             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2286             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2287         }
2288     }
2290     sp_rtb_sensitivize( tbl );
2292     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2296 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2297     NULL, /* child_added */
2298     NULL, /* child_removed */
2299     rect_tb_event_attr_changed,
2300     NULL, /* content_changed */
2301     NULL  /* order_changed */
2302 };
2304 /**
2305  *  \param selection should not be NULL.
2306  */
2307 static void
2308 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2310     int n_selected = 0;
2311     Inkscape::XML::Node *repr = NULL;
2312     SPItem *item = NULL;
2314     if ( g_object_get_data( tbl, "repr" ) ) {
2315         g_object_set_data( tbl, "item", NULL );
2316     }
2317     purge_repr_listener( tbl, tbl );
2319     for (GSList const *items = selection->itemList();
2320          items != NULL;
2321          items = items->next) {
2322         if (SP_IS_RECT((SPItem *) items->data)) {
2323             n_selected++;
2324             item = (SPItem *) items->data;
2325             repr = SP_OBJECT_REPR(item);
2326         }
2327     }
2329     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2331     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2333     if (n_selected == 0) {
2334         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2336         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2337         gtk_action_set_sensitive(w, FALSE);
2338         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2339         gtk_action_set_sensitive(h, FALSE);
2341     } else if (n_selected == 1) {
2342         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2343         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2345         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2346         gtk_action_set_sensitive(w, TRUE);
2347         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2348         gtk_action_set_sensitive(h, TRUE);
2350         if (repr) {
2351             g_object_set_data( tbl, "repr", repr );
2352             g_object_set_data( tbl, "item", item );
2353             Inkscape::GC::anchor(repr);
2354             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2355             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2356         }
2357     } else {
2358         // FIXME: implement averaging of all parameters for multiple selected
2359         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2360         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2361         sp_rtb_sensitivize( tbl );
2362     }
2366 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2368     EgeAdjustmentAction* eact = 0;
2369     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2371     {
2372         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2373         ege_output_action_set_use_markup( act, TRUE );
2374         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2375         g_object_set_data( holder, "mode_action", act );
2376     }
2378     // rx/ry units menu: create
2379     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2380     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2381     // fixme: add % meaning per cent of the width/height
2382     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2383     g_object_set_data( holder, "tracker", tracker );
2385     /* W */
2386     {
2387         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2388         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2389         eact = create_adjustment_action( "RectWidthAction",
2390                                          _("Width"), _("W:"), _("Width of rectangle"),
2391                                          "tools.shapes.rect", "width", 0,
2392                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2393                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2394                                          labels, values, G_N_ELEMENTS(labels),
2395                                          sp_rtb_width_value_changed );
2396         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2397         g_object_set_data( holder, "width_action", eact );
2398         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2399         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2400     }
2402     /* H */
2403     {
2404         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2405         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2406         eact = create_adjustment_action( "RectHeightAction",
2407                                          _("Height"), _("H:"), _("Height of rectangle"),
2408                                          "tools.shapes.rect", "height", 0,
2409                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2410                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2411                                          labels, values, G_N_ELEMENTS(labels),
2412                                          sp_rtb_height_value_changed );
2413         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2414         g_object_set_data( holder, "height_action", eact );
2415         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2416         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2417     }
2419     /* rx */
2420     {
2421         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2422         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2423         eact = create_adjustment_action( "RadiusXAction",
2424                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2425                                          "tools.shapes.rect", "rx", 0,
2426                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2427                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2428                                          labels, values, G_N_ELEMENTS(labels),
2429                                          sp_rtb_rx_value_changed);
2430         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2431         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2432     }
2434     /* ry */
2435     {
2436         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2437         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2438         eact = create_adjustment_action( "RadiusYAction",
2439                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2440                                          "tools.shapes.rect", "ry", 0,
2441                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2442                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2443                                          labels, values, G_N_ELEMENTS(labels),
2444                                          sp_rtb_ry_value_changed);
2445         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2446         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2447     }
2449     // add the units menu
2450     {
2451         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2452         gtk_action_group_add_action( mainActions, act );
2453     }
2455     /* Reset */
2456     {
2457         InkAction* inky = ink_action_new( "RectResetAction",
2458                                           _("Not rounded"),
2459                                           _("Make corners sharp"),
2460                                           "squared_corner",
2461                                           secondarySize );
2462         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2463         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2464         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2465         g_object_set_data( holder, "not_rounded", inky );
2466     }
2468     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2469     sp_rtb_sensitivize( holder );
2471     sigc::connection *connection = new sigc::connection(
2472         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2473         );
2474     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2475     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2478 //########################
2479 //##       3D Box       ##
2480 //########################
2482 // normalize angle so that it lies in the interval [0,360]
2483 static double box3d_normalize_angle (double a) {
2484     double angle = a + ((int) (a/360.0))*360;
2485     if (angle < 0) {
2486         angle += 360.0;
2487     }
2488     return angle;
2491 static void
2492 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2493                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2494     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2495     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2496     //       are reset).
2497     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2499     if (is_infinite) {
2500         gtk_toggle_action_set_active(tact, TRUE);
2501         gtk_action_set_sensitive(act, TRUE);
2503         double angle = persp3d_get_infinite_angle(persp, axis);
2504         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2505             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2506         }
2507     } else {
2508         gtk_toggle_action_set_active(tact, FALSE);
2509         gtk_action_set_sensitive(act, FALSE);
2510     }
2513 static void
2514 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2515     if (!persp_repr) {
2516         g_print ("No perspective given to box3d_resync_toolbar().\n");
2517         return;
2518     }
2520     GtkWidget *tbl = GTK_WIDGET(data);
2521     GtkAdjustment *adj = 0;
2522     GtkAction *act = 0;
2523     GtkToggleAction *tact = 0;
2524     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2525     {
2526         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2527         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2528         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2530         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2531     }
2532     {
2533         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2534         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2535         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2537         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2538     }
2539     {
2540         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2541         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2542         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2544         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2545     }
2548 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2549                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2550                                                   bool /*is_interactive*/, gpointer data)
2552     GtkWidget *tbl = GTK_WIDGET(data);
2554     // quit if run by the attr_changed listener
2555     // note: it used to work without the differently called freeze_ attributes (here and in
2556     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2557     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2558         return;
2559     }
2561     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2562     // sp_document_maybe_done() when the document is undo insensitive)
2563     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2565     // TODO: Only update the appropriate part of the toolbar
2566 //    if (!strcmp(name, "inkscape:vp_z")) {
2567         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2568 //    }
2570     Persp3D *persp = persp3d_get_from_repr(repr);
2571     persp3d_update_box_reprs(persp);
2573     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2576 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2578     NULL, /* child_added */
2579     NULL, /* child_removed */
2580     box3d_persp_tb_event_attr_changed,
2581     NULL, /* content_changed */
2582     NULL  /* order_changed */
2583 };
2585 /**
2586  *  \param selection Should not be NULL.
2587  */
2588 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2589 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2590 static void
2591 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2593     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2594     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2595     // update the perspectives with infinite VPs and leave the other ones untouched).
2597     Inkscape::XML::Node *persp_repr = NULL;
2598     purge_repr_listener(tbl, tbl);
2600     SPItem *item = selection->singleItem();
2601     if (item && SP_IS_BOX3D(item)) {
2602         // FIXME: Also deal with multiple selected boxes
2603         SPBox3D *box = SP_BOX3D(item);
2604         Persp3D *persp = box3d_get_perspective(box);
2605         persp_repr = SP_OBJECT_REPR(persp);
2606         if (persp_repr) {
2607             g_object_set_data(tbl, "repr", persp_repr);
2608             Inkscape::GC::anchor(persp_repr);
2609             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2610             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2611         }
2613         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2614         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2616         box3d_resync_toolbar(persp_repr, tbl);
2617     }
2620 static void
2621 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2623     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2624     SPDocument *document = sp_desktop_document(desktop);
2626     // quit if run by the attr_changed listener
2627     // note: it used to work without the differently called freeze_ attributes (here and in
2628     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2629     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2630         return;
2631     }
2633     // in turn, prevent listener from responding
2634     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2636     //Persp3D *persp = document->current_persp3d;
2637     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2638     if (sel_persps.empty()) {
2639         // this can happen when the document is created; we silently ignore it
2640         return;
2641     }
2642     Persp3D *persp = sel_persps.front();
2644     persp->tmat.set_infinite_direction (axis, adj->value);
2645     SP_OBJECT(persp)->updateRepr();
2647     // TODO: use the correct axis here, too
2648     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2650     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2654 static void
2655 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2657     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2660 static void
2661 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2663     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2666 static void
2667 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2669     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2673 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2675     // TODO: Take all selected perspectives into account
2676     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2677     if (sel_persps.empty()) {
2678         // this can happen when the document is created; we silently ignore it
2679         return;
2680     }
2681     Persp3D *persp = sel_persps.front();
2683     bool set_infinite = gtk_toggle_action_get_active(act);
2684     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2687 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2689     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2692 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2694     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2697 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2699     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2702 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2704     EgeAdjustmentAction* eact = 0;
2705     SPDocument *document = sp_desktop_document (desktop);
2706     Persp3D *persp = document->current_persp3d;
2708     EgeAdjustmentAction* box3d_angle_x = 0;
2709     EgeAdjustmentAction* box3d_angle_y = 0;
2710     EgeAdjustmentAction* box3d_angle_z = 0;
2712     /* Angle X */
2713     {
2714         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2715         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2716         eact = create_adjustment_action( "3DBoxAngleXAction",
2717                                          _("Angle in X direction"), _("Angle X:"),
2718                                          // Translators: PL is short for 'perspective line'
2719                                          _("Angle of PLs in X direction"),
2720                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2721                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2722                                          -360.0, 360.0, 1.0, 10.0,
2723                                          labels, values, G_N_ELEMENTS(labels),
2724                                          box3d_angle_x_value_changed );
2725         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2726         g_object_set_data( holder, "box3d_angle_x_action", eact );
2727         box3d_angle_x = eact;
2728     }
2730     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2731         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2732     } else {
2733         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2734     }
2737     /* VP X state */
2738     {
2739         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2740                                                       // Translators: VP is short for 'vanishing point'
2741                                                       _("State of VP in X direction"),
2742                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2743                                                       "toggle_vp_x",
2744                                                       Inkscape::ICON_SIZE_DECORATION );
2745         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2746         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2747         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2748         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2749         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2750     }
2752     /* Angle Y */
2753     {
2754         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2755         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2756         eact = create_adjustment_action( "3DBoxAngleYAction",
2757                                          _("Angle in Y direction"), _("Angle Y:"),
2758                                          // Translators: PL is short for 'perspective line'
2759                                          _("Angle of PLs in Y direction"),
2760                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2761                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2762                                          -360.0, 360.0, 1.0, 10.0,
2763                                          labels, values, G_N_ELEMENTS(labels),
2764                                          box3d_angle_y_value_changed );
2765         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2766         g_object_set_data( holder, "box3d_angle_y_action", eact );
2767         box3d_angle_y = eact;
2768     }
2770     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2771         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2772     } else {
2773         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2774     }
2776     /* VP Y state */
2777     {
2778         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2779                                                       // Translators: VP is short for 'vanishing point'
2780                                                       _("State of VP in Y direction"),
2781                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2782                                                       "toggle_vp_y",
2783                                                       Inkscape::ICON_SIZE_DECORATION );
2784         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2785         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2786         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2787         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2788         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2789     }
2791     /* Angle Z */
2792     {
2793         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2794         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2795         eact = create_adjustment_action( "3DBoxAngleZAction",
2796                                          _("Angle in Z direction"), _("Angle Z:"),
2797                                          // Translators: PL is short for 'perspective line'
2798                                          _("Angle of PLs in Z direction"),
2799                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2800                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2801                                          -360.0, 360.0, 1.0, 10.0,
2802                                          labels, values, G_N_ELEMENTS(labels),
2803                                          box3d_angle_z_value_changed );
2804         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2805         g_object_set_data( holder, "box3d_angle_z_action", eact );
2806         box3d_angle_z = eact;
2807     }
2809     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2810         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2811     } else {
2812         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2813     }
2815     /* VP Z state */
2816     {
2817         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2818                                                       // Translators: VP is short for 'vanishing point'
2819                                                       _("State of VP in Z direction"),
2820                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2821                                                       "toggle_vp_z",
2822                                                       Inkscape::ICON_SIZE_DECORATION );
2823         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2824         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2825         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2826         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2827         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2828     }
2830     sigc::connection *connection = new sigc::connection(
2831         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2832        );
2833     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2834     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2837 //########################
2838 //##       Spiral       ##
2839 //########################
2841 static void
2842 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2844     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2846     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2847         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2848     }
2850     // quit if run by the attr_changed listener
2851     if (g_object_get_data( tbl, "freeze" )) {
2852         return;
2853     }
2855     // in turn, prevent listener from responding
2856     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2858     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2860     bool modmade = false;
2861     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2862          items != NULL;
2863          items = items->next)
2864     {
2865         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2866             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2867             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2868             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2869             modmade = true;
2870         }
2871     }
2873     g_free(namespaced_name);
2875     if (modmade) {
2876         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2877                                    _("Change spiral"));
2878     }
2880     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2883 static void
2884 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2886     sp_spl_tb_value_changed(adj, tbl, "revolution");
2889 static void
2890 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2892     sp_spl_tb_value_changed(adj, tbl, "expansion");
2895 static void
2896 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2898     sp_spl_tb_value_changed(adj, tbl, "t0");
2901 static void
2902 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2904     GtkWidget *tbl = GTK_WIDGET(obj);
2906     GtkAdjustment *adj;
2908     // fixme: make settable
2909     gdouble rev = 5;
2910     gdouble exp = 1.0;
2911     gdouble t0 = 0.0;
2913     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2914     gtk_adjustment_set_value(adj, rev);
2915     gtk_adjustment_value_changed(adj);
2917     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2918     gtk_adjustment_set_value(adj, exp);
2919     gtk_adjustment_value_changed(adj);
2921     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2922     gtk_adjustment_set_value(adj, t0);
2923     gtk_adjustment_value_changed(adj);
2925     spinbutton_defocus(GTK_OBJECT(tbl));
2929 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2930                                          gchar const */*old_value*/, gchar const */*new_value*/,
2931                                          bool /*is_interactive*/, gpointer data)
2933     GtkWidget *tbl = GTK_WIDGET(data);
2935     // quit if run by the _changed callbacks
2936     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2937         return;
2938     }
2940     // in turn, prevent callbacks from responding
2941     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2943     GtkAdjustment *adj;
2944     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2945     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2947     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2948     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2950     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2951     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2953     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2957 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2958     NULL, /* child_added */
2959     NULL, /* child_removed */
2960     spiral_tb_event_attr_changed,
2961     NULL, /* content_changed */
2962     NULL  /* order_changed */
2963 };
2965 static void
2966 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2968     int n_selected = 0;
2969     Inkscape::XML::Node *repr = NULL;
2971     purge_repr_listener( tbl, tbl );
2973     for (GSList const *items = selection->itemList();
2974          items != NULL;
2975          items = items->next)
2976     {
2977         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2978             n_selected++;
2979             repr = SP_OBJECT_REPR((SPItem *) items->data);
2980         }
2981     }
2983     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2985     if (n_selected == 0) {
2986         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2987     } else if (n_selected == 1) {
2988         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2990         if (repr) {
2991             g_object_set_data( tbl, "repr", repr );
2992             Inkscape::GC::anchor(repr);
2993             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2994             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2995         }
2996     } else {
2997         // FIXME: implement averaging of all parameters for multiple selected
2998         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2999         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3000     }
3004 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3006     EgeAdjustmentAction* eact = 0;
3007     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3009     {
3010         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3011         ege_output_action_set_use_markup( act, TRUE );
3012         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3013         g_object_set_data( holder, "mode_action", act );
3014     }
3016     /* Revolution */
3017     {
3018         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3019         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3020         eact = create_adjustment_action( "SpiralRevolutionAction",
3021                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3022                                          "tools.shapes.spiral", "revolution", 3.0,
3023                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3024                                          0.01, 1024.0, 0.1, 1.0,
3025                                          labels, values, G_N_ELEMENTS(labels),
3026                                          sp_spl_tb_revolution_value_changed, 1, 2);
3027         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3028     }
3030     /* Expansion */
3031     {
3032         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3033         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3034         eact = create_adjustment_action( "SpiralExpansionAction",
3035                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3036                                          "tools.shapes.spiral", "expansion", 1.0,
3037                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3038                                          0.0, 1000.0, 0.01, 1.0,
3039                                          labels, values, G_N_ELEMENTS(labels),
3040                                          sp_spl_tb_expansion_value_changed);
3041         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3042     }
3044     /* T0 */
3045     {
3046         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3047         gdouble values[] = {0, 0.5, 0.9};
3048         eact = create_adjustment_action( "SpiralT0Action",
3049                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3050                                          "tools.shapes.spiral", "t0", 0.0,
3051                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3052                                          0.0, 0.999, 0.01, 1.0,
3053                                          labels, values, G_N_ELEMENTS(labels),
3054                                          sp_spl_tb_t0_value_changed);
3055         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3056     }
3058     /* Reset */
3059     {
3060         InkAction* inky = ink_action_new( "SpiralResetAction",
3061                                           _("Defaults"),
3062                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3063                                           GTK_STOCK_CLEAR,
3064                                           secondarySize );
3065         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3066         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3067     }
3070     sigc::connection *connection = new sigc::connection(
3071         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3072         );
3073     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3074     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3077 //########################
3078 //##     Pen/Pencil    ##
3079 //########################
3082 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3084     // Put stuff here
3087 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3089     // Put stuff here
3092 //########################
3093 //##       Tweak        ##
3094 //########################
3096 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3098     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3101 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3103     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3106 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3108     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3111 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3113     int mode = ege_select_one_action_get_active( act );
3114     prefs_set_int_attribute("tools.tweak", "mode", mode);
3116     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3117     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3118     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3119     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3120     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3121     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3122     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3123         if (doh) gtk_action_set_sensitive (doh, TRUE);
3124         if (dos) gtk_action_set_sensitive (dos, TRUE);
3125         if (dol) gtk_action_set_sensitive (dol, TRUE);
3126         if (doo) gtk_action_set_sensitive (doo, TRUE);
3127         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3128         if (fid) gtk_action_set_sensitive (fid, FALSE);
3129     } else {
3130         if (doh) gtk_action_set_sensitive (doh, FALSE);
3131         if (dos) gtk_action_set_sensitive (dos, FALSE);
3132         if (dol) gtk_action_set_sensitive (dol, FALSE);
3133         if (doo) gtk_action_set_sensitive (doo, FALSE);
3134         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3135         if (fid) gtk_action_set_sensitive (fid, TRUE);
3136     }
3139 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3141     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3144 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3145     bool show = gtk_toggle_action_get_active( act );
3146     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3148 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3149     bool show = gtk_toggle_action_get_active( act );
3150     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3152 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3153     bool show = gtk_toggle_action_get_active( act );
3154     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3156 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3157     bool show = gtk_toggle_action_get_active( act );
3158     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3161 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3163     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3165     {
3166         /* Width */
3167         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3168         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3169         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3170                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3171                                                               "tools.tweak", "width", 15,
3172                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3173                                                               1, 100, 1.0, 10.0,
3174                                                               labels, values, G_N_ELEMENTS(labels),
3175                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3176         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3177         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3178     }
3181     {
3182         /* Force */
3183         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3184         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3185         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3186                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3187                                                               "tools.tweak", "force", 20,
3188                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3189                                                               1, 100, 1.0, 10.0,
3190                                                               labels, values, G_N_ELEMENTS(labels),
3191                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3192         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3193         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3194     }
3196     /* Mode */
3197     {
3198         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3200         GtkTreeIter iter;
3201         gtk_list_store_append( model, &iter );
3202         gtk_list_store_set( model, &iter,
3203                             0, _("Push mode"),
3204                             1, _("Push parts of paths in any direction"),
3205                             2, "tweak_push_mode",
3206                             -1 );
3208         gtk_list_store_append( model, &iter );
3209         gtk_list_store_set( model, &iter,
3210                             0, _("Shrink mode"),
3211                             1, _("Shrink (inset) parts of paths"),
3212                             2, "tweak_shrink_mode",
3213                             -1 );
3215         gtk_list_store_append( model, &iter );
3216         gtk_list_store_set( model, &iter,
3217                             0, _("Grow mode"),
3218                             1, _("Grow (outset) parts of paths"),
3219                             2, "tweak_grow_mode",
3220                             -1 );
3222         gtk_list_store_append( model, &iter );
3223         gtk_list_store_set( model, &iter,
3224                             0, _("Attract mode"),
3225                             1, _("Attract parts of paths towards cursor"),
3226                             2, "tweak_attract_mode",
3227                             -1 );
3229         gtk_list_store_append( model, &iter );
3230         gtk_list_store_set( model, &iter,
3231                             0, _("Repel mode"),
3232                             1, _("Repel parts of paths from cursor"),
3233                             2, "tweak_repel_mode",
3234                             -1 );
3236         gtk_list_store_append( model, &iter );
3237         gtk_list_store_set( model, &iter,
3238                             0, _("Roughen mode"),
3239                             1, _("Roughen parts of paths"),
3240                             2, "tweak_roughen_mode",
3241                             -1 );
3243         gtk_list_store_append( model, &iter );
3244         gtk_list_store_set( model, &iter,
3245                             0, _("Color paint mode"),
3246                             1, _("Paint the tool's color upon selected objects"),
3247                             2, "tweak_colorpaint_mode",
3248                             -1 );
3250         gtk_list_store_append( model, &iter );
3251         gtk_list_store_set( model, &iter,
3252                             0, _("Color jitter mode"),
3253                             1, _("Jitter the colors of selected objects"),
3254                             2, "tweak_colorjitter_mode",
3255                             -1 );
3257         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3258         g_object_set( act, "short_label", _("Mode:"), NULL );
3259         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3260         g_object_set_data( holder, "mode_action", act );
3262         ege_select_one_action_set_appearance( act, "full" );
3263         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3264         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3265         ege_select_one_action_set_icon_column( act, 2 );
3266         ege_select_one_action_set_icon_size( act, secondarySize );
3267         ege_select_one_action_set_tooltip_column( act, 1  );
3269         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3270         ege_select_one_action_set_active( act, mode );
3271         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3273         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3274     }
3276     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3278     {
3279         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3280         ege_output_action_set_use_markup( act, TRUE );
3281         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3282         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3283             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3284         g_object_set_data( holder, "tweak_channels_label", act);
3285     }
3287     {
3288         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3289                                                       _("Hue"),
3290                                                       _("In color mode, act on objects' hue"),
3291                                                       NULL,
3292                                                       Inkscape::ICON_SIZE_DECORATION );
3293         //TRANSLATORS:  "H" here stands for hue
3294         g_object_set( act, "short_label", _("H"), NULL );
3295         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3296         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3297         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3298         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3299             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3300         g_object_set_data( holder, "tweak_doh", act);
3301     }
3302     {
3303         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3304                                                       _("Saturation"),
3305                                                       _("In color mode, act on objects' saturation"),
3306                                                       NULL,
3307                                                       Inkscape::ICON_SIZE_DECORATION );
3308         //TRANSLATORS: "S" here stands for Saturation
3309         g_object_set( act, "short_label", _("S"), NULL );
3310         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3311         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3312         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3313         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3314             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3315         g_object_set_data( holder, "tweak_dos", act );
3316     }
3317     {
3318         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3319                                                       _("Lightness"),
3320                                                       _("In color mode, act on objects' lightness"),
3321                                                       NULL,
3322                                                       Inkscape::ICON_SIZE_DECORATION );
3323         //TRANSLATORS: "L" here stands for Lightness
3324         g_object_set( act, "short_label", _("L"), NULL );
3325         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3326         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3327         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3328         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3329             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3330         g_object_set_data( holder, "tweak_dol", act );
3331     }
3332     {
3333         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3334                                                       _("Opacity"),
3335                                                       _("In color mode, act on objects' opacity"),
3336                                                       NULL,
3337                                                       Inkscape::ICON_SIZE_DECORATION );
3338         //TRANSLATORS: "O" here stands for Opacity
3339         g_object_set( act, "short_label", _("O"), NULL );
3340         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3341         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3342         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3343         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3344             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3345         g_object_set_data( holder, "tweak_doo", act );
3346     }
3348     {   /* Fidelity */
3349         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3350         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3351         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3352                                                               _("Fidelity"), _("Fidelity:"),
3353                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3354                                                               "tools.tweak", "fidelity", 50,
3355                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3356                                                               1, 100, 1.0, 10.0,
3357                                                               labels, values, G_N_ELEMENTS(labels),
3358                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3359         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3360         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3361         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3362             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3363         g_object_set_data( holder, "tweak_fidelity", eact );
3364     }
3367     /* Use Pressure button */
3368     {
3369         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3370                                                       _("Pressure"),
3371                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3372                                                       "use_pressure",
3373                                                       Inkscape::ICON_SIZE_DECORATION );
3374         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3375         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3376         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3377     }
3382 //########################
3383 //##     Calligraphy    ##
3384 //########################
3385 static void update_presets_list(GObject *dataKludge ){
3386     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3387     if (sel) {
3388         ege_select_one_action_set_active(sel, 0 );
3389     }
3392 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3394     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3395     update_presets_list(tbl);
3398 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3400     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3401     update_presets_list(tbl);
3404 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3406     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3407     update_presets_list(tbl);
3410 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3412     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3413     update_presets_list(tbl);
3416 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3418     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3419     update_presets_list(tbl);
3422 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3424     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3425     update_presets_list(tbl);
3428 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3430     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3431     update_presets_list(tbl);
3434 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3436     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3437     update_presets_list(tbl);
3440 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3442     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3443     update_presets_list(tbl);
3446 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3448     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3449     update_presets_list(tbl);
3452 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3454     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3455     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3456     update_presets_list(tbl);
3457     if (calligraphy_angle )
3458         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3462 #define PROFILE_FLOAT_SIZE 7
3463 #define PROFILE_INT_SIZE 4
3464 struct ProfileFloatElement {
3465     char const *name;
3466     double def;
3467     double min;
3468     double max;
3469 };
3470 struct ProfileIntElement {
3471     char const *name;
3472     int def;
3473     int min;
3474     int max;
3475 };
3479 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3480     {"mass",0.02, 0.0, 1.0},
3481     {"wiggle",0.0, 0.0, 1.0},
3482     {"angle",30.0, -90.0, 90.0},
3483     {"thinning",0.1, -1.0, 1.0},
3484     {"tremor",0.0, 0.0, 1.0},
3485     {"flatness",0.9, 0.0, 1.0},
3486     {"cap_rounding",0.0, 0.0, 5.0}
3487 };
3488 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3489     {"width",15, 1, 100},
3490     {"usepressure",1,0,1},
3491     {"tracebackground",0,0,1},
3492     {"usetilt",1,0,1},
3493 };
3497 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3498     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3499     if (! desktop) return;
3501     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3502     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3503     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3505     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3506     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3507     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3508     
3509     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3510         ProfileFloatElement const &pe = f_profile[i];
3511         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3512         prefs_set_double_attribute(pref_path,pe.name,v);
3513     }    
3514     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3515         ProfileIntElement const &pe = i_profile[i];
3516         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3517         prefs_set_int_attribute(pref_path,pe.name,v);
3518     }
3519     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3521     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3522     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3523     GtkTreeIter iter;
3524     gtk_list_store_append( model, &iter );
3525     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3527     free(profile_id);
3528     free(pref_path);
3530     ege_select_one_action_set_active(selector, new_index);
3534 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3535     
3536     gint preset_index = ege_select_one_action_get_active( act ); 
3537     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3538        
3539     if ( profile_name) {        
3540         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3541         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3542             ProfileFloatElement const &pe = f_profile[i];
3543             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3544             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3545             if ( adj ) {
3546                 gtk_adjustment_set_value(adj, v);
3547             }
3548         } 
3549         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3550             ProfileIntElement const &pe = i_profile[i];
3551             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3552             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3553             if ( toggle ) {
3554                 gtk_toggle_action_set_active(toggle, v);
3555             } else printf("No toggle");
3556         }
3557         free(profile_name);
3558         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3559     }
3564 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3566     {
3567         EgeAdjustmentAction* calligraphy_angle = 0;
3569         {
3570         /* Width */
3571         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3572         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3573         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3574                                                               _("Pen Width"), _("Width:"),
3575                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3576                                                               "tools.calligraphic", "width", 15,
3577                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3578                                                               1, 100, 1.0, 10.0,
3579                                                               labels, values, G_N_ELEMENTS(labels),
3580                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3581         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3582         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3583         }
3585         {
3586         /* Thinning */
3587             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3588             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3589         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3590                                                               _("Stroke Thinning"), _("Thinning:"),
3591                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3592                                                               "tools.calligraphic", "thinning", 0.1,
3593                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3594                                                               -1.0, 1.0, 0.01, 0.1,
3595                                                               labels, values, G_N_ELEMENTS(labels),
3596                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3597         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3598         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3599         }
3601         {
3602         /* Angle */
3603         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3604         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3605         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3606                                                               _("Pen Angle"), _("Angle:"),
3607                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3608                                                               "tools.calligraphic", "angle", 30,
3609                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3610                                                               -90.0, 90.0, 1.0, 10.0,
3611                                                               labels, values, G_N_ELEMENTS(labels),
3612                                                               sp_ddc_angle_value_changed, 1, 0 );
3613         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3614         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3615         g_object_set_data( holder, "angle", eact );
3616         calligraphy_angle = eact;
3617         }
3619         {
3620         /* Fixation */
3621             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3622         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3623         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3624                                                               _("Fixation"), _("Fixation:"),
3625                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3626                                                               "tools.calligraphic", "flatness", 0.9,
3627                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3628                                                               0.0, 1.0, 0.01, 0.1,
3629                                                               labels, values, G_N_ELEMENTS(labels),
3630                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3631         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3632         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3633         }
3635         {
3636         /* Cap Rounding */
3637             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3638         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3639         // TRANSLATORS: "cap" means "end" (both start and finish) here
3640         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3641                                                               _("Cap rounding"), _("Caps:"),
3642                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3643                                                               "tools.calligraphic", "cap_rounding", 0.0,
3644                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3645                                                               0.0, 5.0, 0.01, 0.1,
3646                                                               labels, values, G_N_ELEMENTS(labels),
3647                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3648         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3649         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3650         }
3652         {
3653         /* Tremor */
3654             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3655         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3656         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3657                                                               _("Stroke Tremor"), _("Tremor:"),
3658                                                               _("Increase to make strokes rugged and trembling"),
3659                                                               "tools.calligraphic", "tremor", 0.0,
3660                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3661                                                               0.0, 1.0, 0.01, 0.1,
3662                                                               labels, values, G_N_ELEMENTS(labels),
3663                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3665         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3666         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3667         }
3669         {
3670         /* Wiggle */
3671         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3672         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3673         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3674                                                               _("Pen Wiggle"), _("Wiggle:"),
3675                                                               _("Increase to make the pen waver and wiggle"),
3676                                                               "tools.calligraphic", "wiggle", 0.0,
3677                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3678                                                               0.0, 1.0, 0.01, 0.1,
3679                                                               labels, values, G_N_ELEMENTS(labels),
3680                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3681         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3682         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3683         }
3685         {
3686         /* Mass */
3687             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3688         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3689         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3690                                                               _("Pen Mass"), _("Mass:"),
3691                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3692                                                               "tools.calligraphic", "mass", 0.02,
3693                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3694                                                               0.0, 1.0, 0.01, 0.1,
3695                                                               labels, values, G_N_ELEMENTS(labels),
3696                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3697         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3698         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3699         }
3702         /* Trace Background button */
3703         {
3704             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3705                                                           _("Trace Background"),
3706                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3707                                                           "trace_background",
3708                                                           Inkscape::ICON_SIZE_DECORATION );
3709             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3710             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3711             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3712             g_object_set_data( holder, "tracebackground", act );
3713         }
3715         /* Use Pressure button */
3716         {
3717             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3718                                                           _("Pressure"),
3719                                                           _("Use the pressure of the input device to alter the width of the pen"),
3720                                                           "use_pressure",
3721                                                           Inkscape::ICON_SIZE_DECORATION );
3722             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3723             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3724             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3725             g_object_set_data( holder, "usepressure", act );
3726         }
3728         /* Use Tilt button */
3729         {
3730             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3731                                                           _("Tilt"),
3732                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3733                                                           "use_tilt",
3734                                                           Inkscape::ICON_SIZE_DECORATION );
3735             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3736             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3737             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3738             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3739             g_object_set_data( holder, "usetilt", act );
3740         }
3742         /*calligraphic profile */
3743         {
3744             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3745             gchar *pref_path;
3748             GtkTreeIter iter;
3749             gtk_list_store_append( model, &iter );
3750             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3752             //TODO: switch back to prefs API
3753             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3754             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3755             int ii=1;
3756             while (child_repr) {
3757                 GtkTreeIter iter;
3758                 char *preset_name = (char *) child_repr->attribute("name");
3759                 gtk_list_store_append( model, &iter );                
3760                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
3761                 child_repr = sp_repr_next(child_repr); 
3762             }
3764             pref_path = NULL;
3765             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3766             ege_select_one_action_set_appearance( act1, "compact" );
3767             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3768             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3769             g_object_set_data( holder, "profile_selector", act1 );
3770         
3771         }
3773         /*Save or delete calligraphic profile */
3774         {
3775             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3776                                              _("Defaults"),
3777                                              _("Save current settings as new profile"),
3778                                              GTK_STOCK_SAVE );
3779             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3782             gtk_action_group_add_action( mainActions, act );
3783             gtk_action_set_sensitive( act, TRUE );
3784             g_object_set_data( holder, "profile_save_delete", act );
3785         }
3786     }    
3790 //########################
3791 //##    Circle / Arc    ##
3792 //########################
3794 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3796     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3797     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3799     if (v1 == 0 && v2 == 0) {
3800         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3801             gtk_action_set_sensitive( ocb, FALSE );
3802             gtk_action_set_sensitive( make_whole, FALSE );
3803         }
3804     } else {
3805         gtk_action_set_sensitive( ocb, TRUE );
3806         gtk_action_set_sensitive( make_whole, TRUE );
3807     }
3810 static void
3811 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3813     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3815     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3816         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3817     }
3819     // quit if run by the attr_changed listener
3820     if (g_object_get_data( tbl, "freeze" )) {
3821         return;
3822     }
3824     // in turn, prevent listener from responding
3825     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3827     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3829     bool modmade = false;
3830     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3831          items != NULL;
3832          items = items->next)
3833     {
3834         SPItem *item = SP_ITEM(items->data);
3836         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3838             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3839             SPArc *arc = SP_ARC(item);
3841             if (!strcmp(value_name, "start"))
3842                 ge->start = (adj->value * M_PI)/ 180;
3843             else
3844                 ge->end = (adj->value * M_PI)/ 180;
3846             sp_genericellipse_normalize(ge);
3847             ((SPObject *)arc)->updateRepr();
3848             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3850             modmade = true;
3851         }
3852     }
3854     g_free(namespaced_name);
3856     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3858     sp_arctb_sensitivize( tbl, adj->value, other->value );
3860     if (modmade) {
3861         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3862                                    _("Arc: Change start/end"));
3863     }
3865     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3869 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3871     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3874 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3876     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3880 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3882     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3883     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
3884     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3885         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
3886     }
3888     // only take action if run by the attr_changed listener
3889     if (!g_object_get_data( tbl, "freeze" )) {
3890         // in turn, prevent listener from responding
3891         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3893         if ( eraserMode != 0 ) {
3894         } else {
3895         }
3896         // TODO finish implementation
3898         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3899     }
3902 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3904     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3905     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3906         if ( ege_select_one_action_get_active( act ) != 0 ) {
3907             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3908         } else {
3909             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3910         }
3911     }
3913     // quit if run by the attr_changed listener
3914     if (g_object_get_data( tbl, "freeze" )) {
3915         return;
3916     }
3918     // in turn, prevent listener from responding
3919     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3921     bool modmade = false;
3923     if ( ege_select_one_action_get_active(act) != 0 ) {
3924         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3925              items != NULL;
3926              items = items->next)
3927         {
3928             if (SP_IS_ARC((SPItem *) items->data)) {
3929                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3930                 repr->setAttribute("sodipodi:open", "true");
3931                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3932                 modmade = true;
3933             }
3934         }
3935     } else {
3936         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3937              items != NULL;
3938              items = items->next)
3939         {
3940             if (SP_IS_ARC((SPItem *) items->data))    {
3941                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3942                 repr->setAttribute("sodipodi:open", NULL);
3943                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3944                 modmade = true;
3945             }
3946         }
3947     }
3949     if (modmade) {
3950         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3951                                    _("Arc: Change open/closed"));
3952     }
3954     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3957 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3959     GtkAdjustment *adj;
3960     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3961     gtk_adjustment_set_value(adj, 0.0);
3962     gtk_adjustment_value_changed(adj);
3964     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3965     gtk_adjustment_set_value(adj, 0.0);
3966     gtk_adjustment_value_changed(adj);
3968     spinbutton_defocus( GTK_OBJECT(obj) );
3971 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3972                                       gchar const */*old_value*/, gchar const */*new_value*/,
3973                                       bool /*is_interactive*/, gpointer data)
3975     GObject *tbl = G_OBJECT(data);
3977     // quit if run by the _changed callbacks
3978     if (g_object_get_data( tbl, "freeze" )) {
3979         return;
3980     }
3982     // in turn, prevent callbacks from responding
3983     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3985     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3986     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3988     GtkAdjustment *adj1,*adj2;
3989     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3990     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3991     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3992     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3994     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3996     char const *openstr = NULL;
3997     openstr = repr->attribute("sodipodi:open");
3998     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4000     if (openstr) {
4001         ege_select_one_action_set_active( ocb, 1 );
4002     } else {
4003         ege_select_one_action_set_active( ocb, 0 );
4004     }
4006     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4009 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4010     NULL, /* child_added */
4011     NULL, /* child_removed */
4012     arc_tb_event_attr_changed,
4013     NULL, /* content_changed */
4014     NULL  /* order_changed */
4015 };
4018 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4020     int n_selected = 0;
4021     Inkscape::XML::Node *repr = NULL;
4023     purge_repr_listener( tbl, tbl );
4025     for (GSList const *items = selection->itemList();
4026          items != NULL;
4027          items = items->next)
4028     {
4029         if (SP_IS_ARC((SPItem *) items->data)) {
4030             n_selected++;
4031             repr = SP_OBJECT_REPR((SPItem *) items->data);
4032         }
4033     }
4035     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4037     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4038     if (n_selected == 0) {
4039         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4040     } else if (n_selected == 1) {
4041         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4042         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4044         if (repr) {
4045             g_object_set_data( tbl, "repr", repr );
4046             Inkscape::GC::anchor(repr);
4047             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4048             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4049         }
4050     } else {
4051         // FIXME: implement averaging of all parameters for multiple selected
4052         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4053         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4054         sp_arctb_sensitivize( tbl, 1, 0 );
4055     }
4059 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4061     EgeAdjustmentAction* eact = 0;
4062     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4065     {
4066         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4067         ege_output_action_set_use_markup( act, TRUE );
4068         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4069         g_object_set_data( holder, "mode_action", act );
4070     }
4072     /* Start */
4073     {
4074         eact = create_adjustment_action( "ArcStartAction",
4075                                          _("Start"), _("Start:"),
4076                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4077                                          "tools.shapes.arc", "start", 0.0,
4078                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4079                                          -360.0, 360.0, 1.0, 10.0,
4080                                          0, 0, 0,
4081                                          sp_arctb_start_value_changed);
4082         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4083     }
4085     /* End */
4086     {
4087         eact = create_adjustment_action( "ArcEndAction",
4088                                          _("End"), _("End:"),
4089                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4090                                          "tools.shapes.arc", "end", 0.0,
4091                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4092                                          -360.0, 360.0, 1.0, 10.0,
4093                                          0, 0, 0,
4094                                          sp_arctb_end_value_changed);
4095         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4096     }
4098     /* Segments / Pie checkbox */
4099     {
4100         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4102         GtkTreeIter iter;
4103         gtk_list_store_append( model, &iter );
4104         gtk_list_store_set( model, &iter,
4105                             0, _("Closed arc"),
4106                             1, _("Switch to segment (closed shape with two radii)"),
4107                             2, "circle_closed_arc",
4108                             -1 );
4110         gtk_list_store_append( model, &iter );
4111         gtk_list_store_set( model, &iter,
4112                             0, _("Open Arc"),
4113                             1, _("Switch to arc (unclosed shape)"),
4114                             2, "circle_open_arc",
4115                             -1 );
4117         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4118         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4119         g_object_set_data( holder, "open_action", act );
4121         ege_select_one_action_set_appearance( act, "full" );
4122         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4123         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4124         ege_select_one_action_set_icon_column( act, 2 );
4125         ege_select_one_action_set_icon_size( act, secondarySize );
4126         ege_select_one_action_set_tooltip_column( act, 1  );
4128         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4129         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4130         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4131         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4132     }
4134     /* Make Whole */
4135     {
4136         InkAction* inky = ink_action_new( "ArcResetAction",
4137                                           _("Make whole"),
4138                                           _("Make the shape a whole ellipse, not arc or segment"),
4139                                           "reset_circle",
4140                                           secondarySize );
4141         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4142         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4143         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4144         g_object_set_data( holder, "make_whole", inky );
4145     }
4147     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4148     // sensitivize make whole and open checkbox
4149     {
4150         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4151         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4152         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4153     }
4156     sigc::connection *connection = new sigc::connection(
4157         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4158         );
4159     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4160     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4166 // toggle button callbacks and updaters
4168 //########################
4169 //##      Dropper       ##
4170 //########################
4172 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4173     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4174     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4175     if ( set_action ) {
4176         if ( gtk_toggle_action_get_active( act ) ) {
4177             gtk_action_set_sensitive( set_action, TRUE );
4178         } else {
4179             gtk_action_set_sensitive( set_action, FALSE );
4180         }
4181     }
4183     spinbutton_defocus(GTK_OBJECT(tbl));
4186 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4187     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4188     spinbutton_defocus(GTK_OBJECT(tbl));
4192 /**
4193  * Dropper auxiliary toolbar construction and setup.
4194  *
4195  * TODO: Would like to add swatch of current color.
4196  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4197  *       can drag and drop places. Will provide a nice mixing palette.
4198  */
4199 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4201     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4203     {
4204         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4205         ege_output_action_set_use_markup( act, TRUE );
4206         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4207     }
4209     {
4210         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4211                                                       _("Pick opacity"),
4212                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4213                                                       NULL,
4214                                                       Inkscape::ICON_SIZE_DECORATION );
4215         g_object_set( act, "short_label", _("Pick"), NULL );
4216         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4217         g_object_set_data( holder, "pick_action", act );
4218         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4219         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4220     }
4222     {
4223         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4224                                                       _("Assign opacity"),
4225                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4226                                                       NULL,
4227                                                       Inkscape::ICON_SIZE_DECORATION );
4228         g_object_set( act, "short_label", _("Assign"), NULL );
4229         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4230         g_object_set_data( holder, "set_action", act );
4231         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4232         // make sure it's disabled if we're not picking alpha
4233         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4234         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4235     }
4240 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4242     {
4243         /* Width */
4244         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4245         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4246         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4247                                                               _("Pen Width"), _("Width:"),
4248                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4249                                                               "tools.eraser", "width", 15,
4250                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4251                                                               1, 100, 1.0, 10.0,
4252                                                               labels, values, G_N_ELEMENTS(labels),
4253                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4254         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4255         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4256     }
4258     {
4259         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4261         GtkTreeIter iter;
4262         gtk_list_store_append( model, &iter );
4263         gtk_list_store_set( model, &iter,
4264                             0, _("Delete"),
4265                             1, _("Delete objects touched by the eraser"),
4266                             2, "delete_object",
4267                             -1 );
4269         gtk_list_store_append( model, &iter );
4270         gtk_list_store_set( model, &iter,
4271                             0, _("Cut"),
4272                             1, _("Cut out from objects"),
4273                             2, "difference",
4274                             -1 );
4276         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4277         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4278         g_object_set_data( holder, "eraser_mode_action", act );
4280         ege_select_one_action_set_appearance( act, "full" );
4281         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4282         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4283         ege_select_one_action_set_icon_column( act, 2 );
4284         ege_select_one_action_set_tooltip_column( act, 1  );
4286         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4287         ege_select_one_action_set_active( act, eraserMode );
4288         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4289     }
4290     
4293 //########################
4294 //##    Text Toolbox    ##
4295 //########################
4296 /*
4297 static void
4298 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4300     //Call back for letter sizing spinbutton
4303 static void
4304 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4306     //Call back for line height spinbutton
4309 static void
4310 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4312     //Call back for horizontal kerning spinbutton
4315 static void
4316 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4318     //Call back for vertical kerning spinbutton
4321 static void
4322 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4324     //Call back for letter rotation spinbutton
4325 }*/
4327 namespace {
4329 bool popdown_visible = false;
4330 bool popdown_hasfocus = false;
4332 void
4333 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4335     SPStyle *query =
4336         sp_style_new (SP_ACTIVE_DOCUMENT);
4338     int result_fontspec =
4339         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4341     int result_family =
4342         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4344     int result_style =
4345         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4347     int result_numbers =
4348         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4350     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4352     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4353     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4354     {
4355         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4357         if (repr)
4358         {
4359             sp_style_read_from_repr (query, repr);
4360         }
4361         else
4362         {
4363             return;
4364         }
4365     }
4367     if (query->text)
4368     {
4369         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4370             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4371             gtk_entry_set_text (GTK_ENTRY (entry), "");
4373         } else if (query->text->font_specification.value || query->text->font_family.value) {
4375             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4377             // Get the font that corresponds
4378             Glib::ustring familyName;
4380             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4381             if (font) {
4382                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4383                 font->Unref();
4384                 font = NULL;
4385             }
4387             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4389             Gtk::TreePath path;
4390             try {
4391                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4392             } catch (...) {
4393                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4394                 return;
4395             }
4397             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4398             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4400             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4402             gtk_tree_selection_select_path (tselection, path.gobj());
4403             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4405             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4406         }
4408         //Size
4409         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4410         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4411         g_object_set_data (tbl, "size-block", gpointer(1));
4412         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4413         g_object_set_data (tbl, "size-block", gpointer(0));
4414         free (str);
4416         //Anchor
4417         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4418         {
4419             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4420             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4421             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4422             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4423         }
4424         else
4425         {
4426             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4427             {
4428                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4429                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4430                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4431                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4432             }
4433             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4434             {
4435                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4436                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4437                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4438                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4439             }
4440             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4441             {
4442                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4443                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4444                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4445                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4446             }
4447         }
4449         //Style
4450         {
4451             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4453             gboolean active = gtk_toggle_button_get_active (button);
4454             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4456             if (active != check)
4457             {
4458                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4459                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4460                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4461             }
4462         }
4464         {
4465             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4467             gboolean active = gtk_toggle_button_get_active (button);
4468             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4470             if (active != check)
4471             {
4472                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4473                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4474                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4475             }
4476         }
4478         //Orientation
4479         //locking both buttons, changing one affect all group (both)
4480         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4481         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4483         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4484         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4486         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4487         {
4488             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4489         }
4490         else
4491         {
4492             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4493         }
4494         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4495         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4496     }
4498     sp_style_unref(query);
4501 void
4502 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4504     sp_text_toolbox_selection_changed (selection, tbl);
4507 void
4508 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4510     sp_text_toolbox_selection_changed (NULL, tbl);
4513 void
4514 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4515                                 GObject             *tbl)
4517     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4518     GtkTreeModel *model = 0;
4519     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4520     GtkTreeIter   iter;
4521     char         *family = 0;
4523     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4524     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4526     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4527         return;
4528     }
4530     gtk_tree_model_get (model, &iter, 0, &family, -1);
4532     if (g_object_get_data (G_OBJECT (selection), "block"))
4533     {
4534         gtk_entry_set_text (GTK_ENTRY (entry), family);
4535         return;
4536     }
4538     gtk_entry_set_text (GTK_ENTRY (entry), family);
4540     SPStyle *query =
4541         sp_style_new (SP_ACTIVE_DOCUMENT);
4543     int result_fontspec =
4544         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4546     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4548     SPCSSAttr *css = sp_repr_css_attr_new ();
4551     // First try to get the font spec from the stored value
4552     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4554     if (fontSpec.empty()) {
4555         // Construct a new font specification if it does not yet exist
4556         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4557         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4558         fontFromStyle->Unref();
4559     }
4561     if (!fontSpec.empty()) {
4562         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4563         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4564             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4565             if (font) {
4566                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4568                 // Set all the these just in case they were altered when finding the best
4569                 // match for the new family and old style...
4571                 gchar c[256];
4573                 font->Family(c, 256);
4574                 sp_repr_css_set_property (css, "font-family", c);
4576                 font->Attribute( "weight", c, 256);
4577                 sp_repr_css_set_property (css, "font-weight", c);
4579                 font->Attribute("style", c, 256);
4580                 sp_repr_css_set_property (css, "font-style", c);
4582                 font->Attribute("stretch", c, 256);
4583                 sp_repr_css_set_property (css, "font-stretch", c);
4585                 font->Attribute("variant", c, 256);
4586                 sp_repr_css_set_property (css, "font-variant", c);
4588                 font->Unref();
4589             }
4590         }
4591     }
4593     // If querying returned nothing, set the default style of the tool (for new texts)
4594     if (result_fontspec == QUERY_STYLE_NOTHING)
4595     {
4596         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4597         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4598     }
4599     else
4600     {
4601         sp_desktop_set_style (desktop, css, true, true);
4602     }
4604     sp_style_unref(query);
4606     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4607                                    _("Text: Change font family"));
4608     sp_repr_css_attr_unref (css);
4609     free (family);
4610     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4612     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4615 void
4616 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4617                                        GObject      *tbl)
4619     const char *family = gtk_entry_get_text (entry);
4621     try {
4622         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4623         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4624         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4625         gtk_tree_selection_select_path (selection, path.gobj());
4626         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4627         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4628     } catch (...) {
4629         if (family && strlen (family))
4630         {
4631             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4632         }
4633     }
4636 void
4637 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4638                                    gpointer          data)
4640     if (g_object_get_data (G_OBJECT (button), "block")) return;
4641     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4642     int prop = GPOINTER_TO_INT(data);
4644     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4645     SPCSSAttr *css = sp_repr_css_attr_new ();
4647     switch (prop)
4648     {
4649         case 0:
4650         {
4651             sp_repr_css_set_property (css, "text-anchor", "start");
4652             sp_repr_css_set_property (css, "text-align", "start");
4653             break;
4654         }
4655         case 1:
4656         {
4657             sp_repr_css_set_property (css, "text-anchor", "middle");
4658             sp_repr_css_set_property (css, "text-align", "center");
4659             break;
4660         }
4662         case 2:
4663         {
4664             sp_repr_css_set_property (css, "text-anchor", "end");
4665             sp_repr_css_set_property (css, "text-align", "end");
4666             break;
4667         }
4669         case 3:
4670         {
4671             sp_repr_css_set_property (css, "text-anchor", "start");
4672             sp_repr_css_set_property (css, "text-align", "justify");
4673             break;
4674         }
4675     }
4677     SPStyle *query =
4678         sp_style_new (SP_ACTIVE_DOCUMENT);
4679     int result_numbers =
4680         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4682     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4683     if (result_numbers == QUERY_STYLE_NOTHING)
4684     {
4685         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4686     }
4688     sp_style_unref(query);
4690     sp_desktop_set_style (desktop, css, true, true);
4691     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4692                                    _("Text: Change alignment"));
4693     sp_repr_css_attr_unref (css);
4695     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4698 void
4699 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4700                                gpointer          data)
4702     if (g_object_get_data (G_OBJECT (button), "block")) return;
4704     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4705     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4706     int          prop       = GPOINTER_TO_INT(data);
4707     bool         active     = gtk_toggle_button_get_active (button);
4709     SPStyle *query =
4710         sp_style_new (SP_ACTIVE_DOCUMENT);
4712     int result_fontspec =
4713         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4715     int result_family =
4716         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4718     int result_style =
4719         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4721     int result_numbers =
4722         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4724     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4725     Glib::ustring newFontSpec = "";
4727     if (fontSpec.empty()) {
4728         // Construct a new font specification if it does not yet exist
4729         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4730         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4731         fontFromStyle->Unref();
4732     }
4734     switch (prop)
4735     {
4736         case 0:
4737         {
4738             if (!fontSpec.empty()) {
4739                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4740             }
4741             if (fontSpec != newFontSpec) {
4742                 // Don't even set the bold if the font didn't exist on the system
4743                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4744             }
4745             break;
4746         }
4748         case 1:
4749         {
4750             if (!fontSpec.empty()) {
4751                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4752             }
4753             if (fontSpec != newFontSpec) {
4754                 // Don't even set the italic if the font didn't exist on the system
4755                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4756             }
4757             break;
4758         }
4759     }
4761     if (!newFontSpec.empty()) {
4762         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4763     }
4765     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4766     if (result_fontspec == QUERY_STYLE_NOTHING)
4767     {
4768         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4769     }
4771     sp_style_unref(query);
4773     sp_desktop_set_style (desktop, css, true, true);
4774     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4775                                    _("Text: Change font style"));
4776     sp_repr_css_attr_unref (css);
4778     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4781 void
4782 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4783                                      gpointer         data)
4785     if (g_object_get_data (G_OBJECT (button), "block")) {
4786         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4787         return;
4788     }
4790     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4791     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4792     int          prop       = GPOINTER_TO_INT(data);
4794     switch (prop)
4795     {
4796         case 0:
4797         {
4798             sp_repr_css_set_property (css, "writing-mode", "lr");
4799             break;
4800         }
4802         case 1:
4803         {
4804             sp_repr_css_set_property (css, "writing-mode", "tb");
4805             break;
4806         }
4807     }
4809     SPStyle *query =
4810         sp_style_new (SP_ACTIVE_DOCUMENT);
4811     int result_numbers =
4812         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4814     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4815     if (result_numbers == QUERY_STYLE_NOTHING)
4816     {
4817         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4818     }
4820     sp_desktop_set_style (desktop, css, true, true);
4821     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4822                                    _("Text: Change orientation"));
4823     sp_repr_css_attr_unref (css);
4825     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4828 gboolean
4829 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4831     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4832     if (!desktop) return FALSE;
4834     switch (get_group0_keyval (event)) {
4835         case GDK_Escape: // defocus
4836             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4837             sp_text_toolbox_selection_changed (NULL, tbl); // update
4838             return TRUE; // I consumed the event
4839             break;
4840     }
4841     return FALSE;
4844 gboolean
4845 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4847     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4848     if (!desktop) return FALSE;
4850     switch (get_group0_keyval (event)) {
4851         case GDK_KP_Enter:
4852         case GDK_Return:
4853         case GDK_Escape: // defocus
4854             gtk_widget_hide (w);
4855             popdown_visible = false;
4856             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4857             return TRUE; // I consumed the event
4858             break;
4859         case GDK_w:
4860         case GDK_W:
4861             if (event->state & GDK_CONTROL_MASK) {
4862                 gtk_widget_hide (w);
4863                 popdown_visible = false;
4864                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4865                 return TRUE; // I consumed the event
4866             }
4867             break;
4868     }
4869     return FALSE;
4873 void
4874 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4875                                GObject     *tbl)
4877     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4879     if (g_object_get_data (tbl, "size-block")) return;
4881     // If this is not from selecting a size in the list (in which case get_active will give the
4882     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4883     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4884     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4885     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4886         return;
4888     gchar *endptr;
4889     gdouble value = -1;
4890     char *text = gtk_combo_box_get_active_text (cbox);
4891     if (text) {
4892         value = g_strtod (text, &endptr);
4893         if (endptr == text) // conversion failed, non-numeric input
4894             value = -1;
4895         free (text);
4896     }
4897     if (value <= 0) {
4898         return; // could not parse value
4899     }
4901     SPCSSAttr *css = sp_repr_css_attr_new ();
4902     Inkscape::CSSOStringStream osfs;
4903     osfs << value;
4904     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4906     SPStyle *query =
4907         sp_style_new (SP_ACTIVE_DOCUMENT);
4908     int result_numbers =
4909         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4911     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4912     if (result_numbers == QUERY_STYLE_NOTHING)
4913     {
4914         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4915     }
4917     sp_style_unref(query);
4919     sp_desktop_set_style (desktop, css, true, true);
4920     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4921                                    _("Text: Change font size"));
4922     sp_repr_css_attr_unref (css);
4924     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4927 gboolean
4928 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4930     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4931     if (!desktop) return FALSE;
4933     if (!g_object_get_data (tbl, "esc-pressed")) {
4934         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4935         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4936         sp_text_toolbox_size_changed (cbox, tbl);
4937         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4938     }
4939     return FALSE; // I consumed the event
4943 gboolean
4944 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4946     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4947     if (!desktop) return FALSE;
4949     switch (get_group0_keyval (event)) {
4950         case GDK_Escape: // defocus
4951             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4952             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4953             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4954             return TRUE; // I consumed the event
4955             break;
4956         case GDK_Return: // defocus
4957         case GDK_KP_Enter:
4958             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4959             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4960             sp_text_toolbox_size_changed (cbox, tbl);
4961             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4962             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4963             return TRUE; // I consumed the event
4964             break;
4965     }
4966     return FALSE;
4969 void
4970 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4971                                          GObject            *tbl)
4973     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4974     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4975     int x, y;
4977     if (!popdown_visible)
4978     {
4979         gdk_window_get_origin (widget->window, &x, &y);
4980         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4981         gtk_widget_show_all (popdown);
4982         //sp_transientize (popdown);
4984         gdk_pointer_grab (widget->window, TRUE,
4985                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4986                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4987                                         GDK_POINTER_MOTION_MASK),
4988                           NULL, NULL, GDK_CURRENT_TIME);
4990         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4992         popdown_visible = true;
4993     }
4994     else
4995     {
4996         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4997         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4998         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4999         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5000         gtk_widget_hide (popdown);
5001         popdown_visible = false;
5002     }
5005 gboolean
5006 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5007                                  GdkEventFocus    */*event*/,
5008                                  GObject          */*tbl*/)
5010     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5011     return FALSE;
5014 gboolean
5015 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5016                                    GdkEventFocus    */*event*/,
5017                                    GObject          */*tbl*/)
5019     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5021     if (popdown_hasfocus) {
5022         gtk_widget_hide (popdown);
5023         popdown_hasfocus = false;
5024         popdown_visible = false;
5025         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5026         return TRUE;
5027     }
5028     return FALSE;
5031 gboolean
5032 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5033                                    GdkEventFocus    */*event*/,
5034                                    GObject          */*tbl*/)
5036     popdown_hasfocus = true;
5037     return TRUE;
5041 void
5042 cell_data_func  (GtkTreeViewColumn */*column*/,
5043                  GtkCellRenderer   *cell,
5044                  GtkTreeModel      *tree_model,
5045                  GtkTreeIter       *iter,
5046                  gpointer           /*data*/)
5048     char        *family,
5049         *family_escaped,
5050         *sample_escaped;
5052     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5054     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5056     family_escaped = g_markup_escape_text (family, -1);
5057     sample_escaped = g_markup_escape_text (sample, -1);
5059     std::stringstream markup;
5060     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5061     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5063     free (family);
5064     free (family_escaped);
5065     free (sample_escaped);
5068 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5069     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5070     if (completion) {
5071         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5072         g_object_unref (completion);
5073     }
5076 GtkWidget*
5077 sp_text_toolbox_new (SPDesktop *desktop)
5079     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
5080     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5082     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5083     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5085     GtkTooltips *tt = gtk_tooltips_new();
5086     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5088     ////////////Family
5089     //Window
5090     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5091     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5093     //Entry
5094     GtkWidget           *entry = gtk_entry_new ();
5095     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5096     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5097     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5098     gtk_entry_completion_set_text_column (completion, 0);
5099     gtk_entry_completion_set_minimum_key_length (completion, 1);
5100     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5101     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5102     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5103     aux_toolbox_space (tbl, 1);
5104     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5105     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5107     //Button
5108     GtkWidget   *button = gtk_button_new ();
5109     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5110     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5112     //Popdown
5113     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5114     GtkWidget           *treeview = gtk_tree_view_new ();
5116     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5117     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5118     gtk_tree_view_column_pack_start (column, cell, FALSE);
5119     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5120     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5121     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5123     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5124     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5125     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5127     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5129     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5130     gtk_container_add (GTK_CONTAINER (sw), treeview);
5132     gtk_container_add (GTK_CONTAINER (window), sw);
5133     gtk_widget_set_size_request (window, 300, 450);
5135     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5136     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5137     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5139     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5141     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5142     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5143     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5145     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5146     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5148     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5149     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5150     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5151     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5152     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5154     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5155     aux_toolbox_space (tbl, 1);
5156     GtkWidget *box = gtk_event_box_new ();
5157     gtk_container_add (GTK_CONTAINER (box), image);
5158     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5159     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5160     GtkTooltips *tooltips = gtk_tooltips_new ();
5161     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5162     gtk_widget_hide (GTK_WIDGET (box));
5163     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5165     ////////////Size
5166     const char *sizes[] = {
5167         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5168         "16", "18", "20", "22", "24", "28",
5169         "32", "36", "40", "48", "56", "64", "72", "144"
5170     };
5172     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5173     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5174     gtk_widget_set_size_request (cbox, 80, -1);
5175     aux_toolbox_space (tbl, 1);
5176     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5177     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5178     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5179     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5180     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5182     //spacer
5183     aux_toolbox_space (tbl, 4);
5184     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5186     ////////////Text anchor
5187     GtkWidget *group   = gtk_radio_button_new (NULL);
5188     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5189     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5191     // left
5192     GtkWidget *rbutton = group;
5193     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5194     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5195     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5197     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5198     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5199     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5200     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5202     // center
5203     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5204     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5205     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5206     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5208     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5209     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5210     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5211     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5213     // right
5214     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5215     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5216     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5217     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5219     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5220     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5221     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5222     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5224     // fill
5225     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5226     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5227     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5228     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5230     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5231     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5232     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5233     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5235     aux_toolbox_space (tbl, 1);
5236     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5238     //spacer
5239     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5241     ////////////Text style
5242     row = gtk_hbox_new (FALSE, 4);
5244     // bold
5245     rbutton = gtk_toggle_button_new ();
5246     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5247     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5248     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5249     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5251     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5252     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5253     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5255     // italic
5256     rbutton = gtk_toggle_button_new ();
5257     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5258     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5259     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5260     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5262     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5263     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5264     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5266     aux_toolbox_space (tbl, 1);
5267     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5269     //spacer
5270     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5272     ////////////Text orientation
5273     group   = gtk_radio_button_new (NULL);
5274     row     = gtk_hbox_new (FALSE, 4);
5275     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5277     // horizontal
5278     rbutton = group;
5279     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5280     gtk_container_add           (GTK_CONTAINER (rbutton),
5281                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5282     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5283     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5285     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5286     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5287     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5289     // vertical
5290     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5291     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5292     gtk_container_add           (GTK_CONTAINER (rbutton),
5293                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5294     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5295     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5297     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5298     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5299     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5300     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5303     //watch selection
5304     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5306     sigc::connection *c_selection_changed =
5307         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5308                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5309     pool->add_connection ("selection-changed", c_selection_changed);
5311     sigc::connection *c_selection_modified =
5312         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5313                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5314     pool->add_connection ("selection-modified", c_selection_modified);
5316     sigc::connection *c_subselection_changed =
5317         new sigc::connection (desktop->connectToolSubselectionChanged
5318                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5319     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5321     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5324     gtk_widget_show_all (tbl);
5325     return tbl;
5327 } // end of sp_text_toolbox_new()
5329 }//<unnamed> namespace
5332 //#########################
5333 //##      Connector      ##
5334 //#########################
5336 static void sp_connector_path_set_avoid(void)
5338     cc_selection_set_avoid(true);
5342 static void sp_connector_path_set_ignore(void)
5344     cc_selection_set_avoid(false);
5349 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5351     // quit if run by the _changed callbacks
5352     if (g_object_get_data( tbl, "freeze" )) {
5353         return;
5354     }
5356     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5357     SPDocument *doc = sp_desktop_document(desktop);
5359     if (!sp_document_get_undo_sensitive(doc))
5360     {
5361         return;
5362     }
5364     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5366     if ( repr->attribute("inkscape:connector-spacing") ) {
5367         gdouble priorValue = gtk_adjustment_get_value(adj);
5368         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5369         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5370             return;
5371         }
5372     } else if ( adj->value == defaultConnSpacing ) {
5373         return;
5374     }
5376     // in turn, prevent callbacks from responding
5377     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5379     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5380     SP_OBJECT(desktop->namedview)->updateRepr();
5382     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5383     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5384         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5385         NR::Matrix m = NR::identity();
5386         avoid_item_move(&m, item);
5387     }
5389     if (items) {
5390         g_slist_free(items);
5391     }
5393     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5394             _("Change connector spacing"));
5396     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5398     spinbutton_defocus(GTK_OBJECT(tbl));
5401 static void sp_connector_graph_layout(void)
5403     if (!SP_ACTIVE_DESKTOP) return;
5405     // hack for clones, see comment in align-and-distribute.cpp
5406     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5407     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5409     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5411     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5413     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5416 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5418     if ( gtk_toggle_action_get_active( act ) ) {
5419         prefs_set_string_attribute("tools.connector", "directedlayout",
5420                 "true");
5421     } else {
5422         prefs_set_string_attribute("tools.connector", "directedlayout",
5423                 "false");
5424     }
5427 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5429     if ( gtk_toggle_action_get_active( act ) ) {
5430         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5431                 "true");
5432     } else {
5433         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5434                 "false");
5435     }
5439 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5441     prefs_set_double_attribute("tools.connector", "length", adj->value);
5442     spinbutton_defocus(GTK_OBJECT(tbl));
5445 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5446                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5447                                             bool /*is_interactive*/, gpointer data)
5449     GtkWidget *tbl = GTK_WIDGET(data);
5451     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5452         return;
5453     }
5454     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5455         return;
5456     }
5458     GtkAdjustment *adj = (GtkAdjustment*)
5459             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5460     gdouble spacing = defaultConnSpacing;
5461     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5463     gtk_adjustment_set_value(adj, spacing);
5467 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5468     NULL, /* child_added */
5469     NULL, /* child_removed */
5470     connector_tb_event_attr_changed,
5471     NULL, /* content_changed */
5472     NULL  /* order_changed */
5473 };
5476 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5478     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5480     {
5481         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5482                                           _("Avoid"),
5483                                           _("Make connectors avoid selected objects"),
5484                                           "connector_avoid",
5485                                           secondarySize );
5486         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5487         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5488     }
5490     {
5491         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5492                                           _("Ignore"),
5493                                           _("Make connectors ignore selected objects"),
5494                                           "connector_ignore",
5495                                           secondarySize );
5496         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5497         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5498     }
5500     EgeAdjustmentAction* eact = 0;
5502     // Spacing spinbox
5503     eact = create_adjustment_action( "ConnectorSpacingAction",
5504                                      _("Connector Spacing"), _("Spacing:"),
5505                                      _("The amount of space left around objects by auto-routing connectors"),
5506                                      "tools.connector", "spacing", defaultConnSpacing,
5507                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5508                                      0, 100, 1.0, 10.0,
5509                                      0, 0, 0,
5510                                      connector_spacing_changed, 1, 0 );
5511     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5513     // Graph (connector network) layout
5514     {
5515         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5516                                           _("Graph"),
5517                                           _("Nicely arrange selected connector network"),
5518                                           "graph_layout",
5519                                           secondarySize );
5520         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5521         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5522     }
5524     // Default connector length spinbox
5525     eact = create_adjustment_action( "ConnectorLengthAction",
5526                                      _("Connector Length"), _("Length:"),
5527                                      _("Ideal length for connectors when layout is applied"),
5528                                      "tools.connector", "length", 100,
5529                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5530                                      10, 1000, 10.0, 100.0,
5531                                      0, 0, 0,
5532                                      connector_length_changed, 1, 0 );
5533     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5536     // Directed edges toggle button
5537     {
5538         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5539                                                       _("Downwards"),
5540                                                       _("Make connectors with end-markers (arrows) point downwards"),
5541                                                       "directed_graph",
5542                                                       Inkscape::ICON_SIZE_DECORATION );
5543         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5545         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5546         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5547                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5549         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5550     }
5552     // Avoid overlaps toggle button
5553     {
5554         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5555                                                       _("Remove overlaps"),
5556                                                       _("Do not allow overlapping shapes"),
5557                                                       "remove_overlaps",
5558                                                       Inkscape::ICON_SIZE_DECORATION );
5559         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5561         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5562         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5563                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5565         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5566     }
5568     // Code to watch for changes to the connector-spacing attribute in
5569     // the XML.
5570     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5571     g_assert(repr != NULL);
5573     purge_repr_listener( holder, holder );
5575     if (repr) {
5576         g_object_set_data( holder, "repr", repr );
5577         Inkscape::GC::anchor(repr);
5578         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5579         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5580     }
5581 } // end of sp_connector_toolbox_prep()
5584 //#########################
5585 //##     Paintbucket     ##
5586 //#########################
5588 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5590     gint channels = ege_select_one_action_get_active( act );
5591     flood_channels_set_channels( channels );
5594 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5596     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5599 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5601     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5604 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5606     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5607     SPUnit const *unit = tracker->getActiveUnit();
5609     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5611     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5614 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5616     // FIXME: make defaults settable via Inkscape Options
5617     struct KeyValue {
5618         char const *key;
5619         double value;
5620     } const key_values[] = {
5621         {"threshold", 15},
5622         {"offset", 0.0}
5623     };
5625     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5626         KeyValue const &kv = key_values[i];
5627         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5628         if ( adj ) {
5629             gtk_adjustment_set_value(adj, kv.value);
5630         }
5631     }
5633     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5634     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5635     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5636     ege_select_one_action_set_active( autogap_action, 0 );
5639 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5641     EgeAdjustmentAction* eact = 0;
5643     {
5644         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5646         GList* items = 0;
5647         gint count = 0;
5648         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5649         {
5650             GtkTreeIter iter;
5651             gtk_list_store_append( model, &iter );
5652             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5653             count++;
5654         }
5655         g_list_free( items );
5656         items = 0;
5657         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5658         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5659         ege_select_one_action_set_appearance( act1, "compact" );
5660         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5661         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5662         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5663         g_object_set_data( holder, "channels_action", act1 );
5664     }
5666     // Spacing spinbox
5667     {
5668         eact = create_adjustment_action(
5669             "ThresholdAction",
5670             _("Fill Threshold"), _("Threshold:"),
5671             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5672             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5673             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5674             0, 0, 0,
5675             paintbucket_threshold_changed, 1, 0 );
5677         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5678     }
5680     // Create the units menu.
5681     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5682     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5683     if (stored_unit)
5684         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5685     g_object_set_data( holder, "tracker", tracker );
5686     {
5687         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5688         gtk_action_group_add_action( mainActions, act );
5689     }
5691     // Offset spinbox
5692     {
5693         eact = create_adjustment_action(
5694             "OffsetAction",
5695             _("Grow/shrink by"), _("Grow/shrink by:"),
5696             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5697             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5698             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5699             0, 0, 0,
5700             paintbucket_offset_changed, 1, 2);
5701         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5703         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5704     }
5706     /* Auto Gap */
5707     {
5708         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5710         GList* items = 0;
5711         gint count = 0;
5712         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5713         {
5714             GtkTreeIter iter;
5715             gtk_list_store_append( model, &iter );
5716             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5717             count++;
5718         }
5719         g_list_free( items );
5720         items = 0;
5721         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5722         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5723         ege_select_one_action_set_appearance( act2, "compact" );
5724         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5725         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5726         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5727         g_object_set_data( holder, "autogap_action", act2 );
5728     }
5730     /* Reset */
5731     {
5732         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5733                                           _("Defaults"),
5734                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5735                                           GTK_STOCK_CLEAR );
5736         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5737         gtk_action_group_add_action( mainActions, act );
5738         gtk_action_set_sensitive( act, TRUE );
5739     }
5743 /*
5744   Local Variables:
5745   mode:c++
5746   c-file-style:"stroustrup"
5747   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5748   indent-tabs-mode:nil
5749   fill-column:99
5750   End:
5751 */
5752 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :