Code

76ca4e02e0a6879f9733742c2eeb6e7b0e403b97
[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"
57 #include "live_effects/effect.h"
59 #include "inkscape.h"
60 #include "conn-avoid-ref.h"
63 #include "select-toolbar.h"
64 #include "gradient-toolbar.h"
66 #include "connector-context.h"
67 #include "node-context.h"
68 #include "draw-context.h"
69 #include "shape-editor.h"
70 #include "tweak-context.h"
71 #include "sp-rect.h"
72 #include "box3d.h"
73 #include "box3d-context.h"
74 #include "sp-star.h"
75 #include "sp-spiral.h"
76 #include "sp-ellipse.h"
77 #include "sp-text.h"
78 #include "sp-flowtext.h"
79 #include "sp-clippath.h"
80 #include "sp-mask.h"
81 #include "style.h"
82 #include "selection.h"
83 #include "selection-chemistry.h"
84 #include "document-private.h"
85 #include "desktop-style.h"
86 #include "../libnrtype/font-lister.h"
87 #include "../libnrtype/font-instance.h"
88 #include "../connection-pool.h"
89 #include "../prefs-utils.h"
90 #include "../inkscape-stock.h"
91 #include "icon.h"
92 #include "graphlayout/graphlayout.h"
94 #include "mod360.h"
96 #include "toolbox.h"
98 #include "flood-context.h"
100 #include "ink-action.h"
101 #include "ege-adjustment-action.h"
102 #include "ege-output-action.h"
103 #include "ege-select-one-action.h"
104 #include "helper/unit-tracker.h"
106 #include "svg/css-ostringstream.h"
108 #include "widgets/calligraphic-profile-rename.h"
110 using Inkscape::UnitTracker;
112 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
113 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
115 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
128 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
135 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
136     static Inkscape::IconSize sizeChoices[] = {
137         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
138         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
139         Inkscape::ICON_SIZE_MENU
140     };
141     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
142     return sizeChoices[index];
145 static struct {
146     gchar const *type_name;
147     gchar const *data_name;
148     sp_verb_t verb;
149     sp_verb_t doubleclick_verb;
150 } const tools[] = {
151     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
152     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
153     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
154     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
155     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
156     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
157     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
158     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
159     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
160     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
161     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
162     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
163     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
164     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
165     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
166     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
167     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
168     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
169     { NULL, NULL, 0, 0 }
170 };
172 static struct {
173     gchar const *type_name;
174     gchar const *data_name;
175     GtkWidget *(*create_func)(SPDesktop *desktop);
176     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
177     gchar const *ui_name;
178     gint swatch_verb_id;
179     gchar const *swatch_tool;
180     gchar const *swatch_tip;
181 } const aux_toolboxes[] = {
182     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
183       SP_VERB_INVALID, 0, 0},
184     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
187       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
188     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
189       SP_VERB_INVALID, 0, 0},
190     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
191       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
192     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
193       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
194     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
195       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
196     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
197       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
198     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
199       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
200     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
201       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
202     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
203       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
204     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
205       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
206     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
207       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
208     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
209       SP_VERB_INVALID, 0, 0},
210     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
211       SP_VERB_INVALID, 0, 0},
212     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
213       SP_VERB_INVALID, 0, 0},
214     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
215       SP_VERB_INVALID, 0, 0},
216     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
217       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
218     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
219 };
222 static gchar const * ui_descr =
223         "<ui>"
224         "  <toolbar name='SelectToolbar'>"
225         "    <toolitem action='EditSelectAll' />"
226         "    <toolitem action='EditSelectAllInAllLayers' />"
227         "    <toolitem action='EditDeselect' />"
228         "    <separator />"
229         "    <toolitem action='ObjectRotate90CCW' />"
230         "    <toolitem action='ObjectRotate90' />"
231         "    <toolitem action='ObjectFlipHorizontally' />"
232         "    <toolitem action='ObjectFlipVertically' />"
233         "    <separator />"
234         "    <toolitem action='SelectionToBack' />"
235         "    <toolitem action='SelectionLower' />"
236         "    <toolitem action='SelectionRaise' />"
237         "    <toolitem action='SelectionToFront' />"
238         "    <separator />"
239         "    <toolitem action='XAction' />"
240         "    <toolitem action='YAction' />"
241         "    <toolitem action='WidthAction' />"
242         "    <toolitem action='LockAction' />"
243         "    <toolitem action='HeightAction' />"
244         "    <toolitem action='UnitsAction' />"
245         "    <separator />"
246         "    <toolitem action='transform_affect_label' />"
247         "    <toolitem action='transform_stroke' />"
248         "    <toolitem action='transform_corners' />"
249         "    <toolitem action='transform_gradient' />"
250         "    <toolitem action='transform_pattern' />"
251         "  </toolbar>"
253         "  <toolbar name='NodeToolbar'>"
254         "    <toolitem action='NodeInsertAction' />"
255         "    <toolitem action='NodeDeleteAction' />"
256         "    <separator />"
257         "    <toolitem action='NodeJoinAction' />"
258         "    <toolitem action='NodeBreakAction' />"
259         "    <separator />"
260         "    <toolitem action='NodeJoinSegmentAction' />"
261         "    <toolitem action='NodeDeleteSegmentAction' />"
262         "    <separator />"
263         "    <toolitem action='NodeCuspAction' />"
264         "    <toolitem action='NodeSmoothAction' />"
265         "    <toolitem action='NodeSymmetricAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeLineAction' />"
268         "    <toolitem action='NodeCurveAction' />"
269         "    <separator />"
270         "    <toolitem action='ObjectToPath' />"
271         "    <toolitem action='StrokeToPath' />"
272         "    <separator />"
273         "    <toolitem action='NodeXAction' />"
274         "    <toolitem action='NodeYAction' />"
275         "    <toolitem action='NodeUnitsAction' />"
276         "    <separator />"
277         "    <toolitem action='ObjectEditClipPathAction' />"
278         "    <toolitem action='ObjectEditMaskPathAction' />"
279         "    <toolitem action='EditNextLPEParameterAction' />"
280         "    <separator />"
281         "    <toolitem action='NodesShowHandlesAction' />"
282         "    <toolitem action='NodesShowHelperpath' />"
283         "  </toolbar>"
285         "  <toolbar name='TweakToolbar'>"
286         "    <toolitem action='TweakWidthAction' />"
287         "    <separator />"
288         "    <toolitem action='TweakForceAction' />"
289         "    <toolitem action='TweakPressureAction' />"
290         "    <separator />"
291         "    <toolitem action='TweakModeAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakFidelityAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakChannelsLabel' />"
296         "    <toolitem action='TweakDoH' />"
297         "    <toolitem action='TweakDoS' />"
298         "    <toolitem action='TweakDoL' />"
299         "    <toolitem action='TweakDoO' />"
300         "  </toolbar>"
302         "  <toolbar name='ZoomToolbar'>"
303         "    <toolitem action='ZoomIn' />"
304         "    <toolitem action='ZoomOut' />"
305         "    <separator />"
306         "    <toolitem action='Zoom1:0' />"
307         "    <toolitem action='Zoom1:2' />"
308         "    <toolitem action='Zoom2:1' />"
309         "    <separator />"
310         "    <toolitem action='ZoomSelection' />"
311         "    <toolitem action='ZoomDrawing' />"
312         "    <toolitem action='ZoomPage' />"
313         "    <toolitem action='ZoomPageWidth' />"
314         "    <separator />"
315         "    <toolitem action='ZoomPrev' />"
316         "    <toolitem action='ZoomNext' />"
317         "  </toolbar>"
319         "  <toolbar name='StarToolbar'>"
320         "    <separator />"
321         "    <toolitem action='StarStateAction' />"
322         "    <separator />"
323         "    <toolitem action='FlatAction' />"
324         "    <separator />"
325         "    <toolitem action='MagnitudeAction' />"
326         "    <toolitem action='SpokeAction' />"
327         "    <toolitem action='RoundednessAction' />"
328         "    <toolitem action='RandomizationAction' />"
329         "    <separator />"
330         "    <toolitem action='StarResetAction' />"
331         "  </toolbar>"
333         "  <toolbar name='RectToolbar'>"
334         "    <toolitem action='RectStateAction' />"
335         "    <toolitem action='RectWidthAction' />"
336         "    <toolitem action='RectHeightAction' />"
337         "    <toolitem action='RadiusXAction' />"
338         "    <toolitem action='RadiusYAction' />"
339         "    <toolitem action='RectUnitsAction' />"
340         "    <separator />"
341         "    <toolitem action='RectResetAction' />"
342         "  </toolbar>"
344         "  <toolbar name='3DBoxToolbar'>"
345         "    <toolitem action='3DBoxAngleXAction' />"
346         "    <toolitem action='3DBoxVPXStateAction' />"
347         "    <separator />"
348         "    <toolitem action='3DBoxAngleYAction' />"
349         "    <toolitem action='3DBoxVPYStateAction' />"
350         "    <separator />"
351         "    <toolitem action='3DBoxAngleZAction' />"
352         "    <toolitem action='3DBoxVPZStateAction' />"
353         "  </toolbar>"
355         "  <toolbar name='SpiralToolbar'>"
356         "    <toolitem action='SpiralStateAction' />"
357         "    <toolitem action='SpiralRevolutionAction' />"
358         "    <toolitem action='SpiralExpansionAction' />"
359         "    <toolitem action='SpiralT0Action' />"
360         "    <separator />"
361         "    <toolitem action='SpiralResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='PenToolbar'>"
365         "    <toolitem action='FreehandModeActionPenTemp' />"
366         "    <toolitem action='FreehandModeActionPen' />"
367         "  </toolbar>"
369         "  <toolbar name='PencilToolbar'>"
370         "    <toolitem action='FreehandModeActionPencilTemp' />"
371         "    <toolitem action='FreehandModeActionPencil' />"
372         "  </toolbar>"
374         "  <toolbar name='CalligraphyToolbar'>"
375         "    <separator />"
376         "    <toolitem action='SetProfileAction'/>"
377         "    <toolitem action='SaveDeleteProfileAction'/>"
378         "    <separator />"
379         "    <toolitem action='CalligraphyWidthAction' />"
380         "    <toolitem action='PressureAction' />"
381         "    <toolitem action='TraceAction' />"
382         "    <toolitem action='ThinningAction' />"
383         "    <separator />"
384         "    <toolitem action='AngleAction' />"
385         "    <toolitem action='TiltAction' />"
386         "    <toolitem action='FixationAction' />"
387         "    <separator />"
388         "    <toolitem action='CapRoundingAction' />"
389         "    <separator />"
390         "    <toolitem action='TremorAction' />"
391         "    <toolitem action='WiggleAction' />"
392         "    <toolitem action='MassAction' />"
393         "    <separator />"
394         "  </toolbar>"
396         "  <toolbar name='ArcToolbar'>"
397         "    <toolitem action='ArcStateAction' />"
398         "    <separator />"
399         "    <toolitem action='ArcStartAction' />"
400         "    <toolitem action='ArcEndAction' />"
401         "    <separator />"
402         "    <toolitem action='ArcOpenAction' />"
403         "    <separator />"
404         "    <toolitem action='ArcResetAction' />"
405         "    <separator />"
406         "  </toolbar>"
408         "  <toolbar name='PaintbucketToolbar'>"
409         "    <toolitem action='ChannelsAction' />"
410         "    <separator />"
411         "    <toolitem action='ThresholdAction' />"
412         "    <separator />"
413         "    <toolitem action='OffsetAction' />"
414         "    <toolitem action='PaintbucketUnitsAction' />"
415         "    <separator />"
416         "    <toolitem action='AutoGapAction' />"
417         "    <separator />"
418         "    <toolitem action='PaintbucketResetAction' />"
419         "  </toolbar>"
421         "  <toolbar name='EraserToolbar'>"
422         "    <toolitem action='EraserWidthAction' />"
423         "    <separator />"
424         "    <toolitem action='EraserModeAction' />"
425         "  </toolbar>"
427         "  <toolbar name='DropperToolbar'>"
428         "    <toolitem action='DropperOpacityAction' />"
429         "    <toolitem action='DropperPickAlphaAction' />"
430         "    <toolitem action='DropperSetAlphaAction' />"
431         "  </toolbar>"
433         "  <toolbar name='ConnectorToolbar'>"
434         "    <toolitem action='ConnectorAvoidAction' />"
435         "    <toolitem action='ConnectorIgnoreAction' />"
436         "    <toolitem action='ConnectorSpacingAction' />"
437         "    <toolitem action='ConnectorGraphAction' />"
438         "    <toolitem action='ConnectorLengthAction' />"
439         "    <toolitem action='ConnectorDirectedAction' />"
440         "    <toolitem action='ConnectorOverlapAction' />"
441         "  </toolbar>"
443         "</ui>"
446 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
448 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
450 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
451 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
453 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
454 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
456 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
457 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
459 /* Global text entry widgets necessary for update */
460 /* GtkWidget *dropper_rgb_entry,
461           *dropper_opacity_entry ; */
462 // should be made a private member once this is converted to class
464 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
465     connection->disconnect();
466     delete connection;
469 static void purge_repr_listener( GObject* obj, GObject* tbl )
471     (void)obj;
472     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
473     if (oldrepr) { // remove old listener
474         sp_repr_remove_listener_by_data(oldrepr, tbl);
475         Inkscape::GC::release(oldrepr);
476         oldrepr = 0;
477         g_object_set_data( tbl, "repr", NULL );
478     }
481 GtkWidget *
482 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
483                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
484                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
486     SPAction *action = verb->get_action(view);
487     if (!action) return NULL;
489     SPAction *doubleclick_action;
490     if (doubleclick_verb)
491         doubleclick_action = doubleclick_verb->get_action(view);
492     else
493         doubleclick_action = NULL;
495     /* fixme: Handle sensitive/unsensitive */
496     /* fixme: Implement sp_button_new_from_action */
497     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
498     gtk_widget_show(b);
499     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
501     return b;
504 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
505                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
507     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
510 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
511                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
513     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
517 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
519     SPAction* targetAction = SP_ACTION(user_data);
520     if ( targetAction ) {
521         sp_action_perform( targetAction, NULL );
522     }
525 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
527     if ( data ) {
528         GtkAction* act = GTK_ACTION(data);
529         gtk_action_set_sensitive( act, sensitive );
530     }
533 static SPActionEventVector action_event_vector = {
534     {NULL},
535     NULL,
536     NULL,
537     sp_action_action_set_sensitive,
538     NULL,
539     NULL
540 };
542 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
544     GtkAction* act = 0;
546     SPAction* targetAction = verb->get_action(view);
547     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
548     act = GTK_ACTION(inky);
549     gtk_action_set_sensitive( act, targetAction->sensitive );
551     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
553     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
554     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
556     return act;
559 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
561     Inkscape::UI::View::View *view = desktop;
562     gint verbsToUse[] = {
563         // disabled until we have icons for them:
564         //find
565         //SP_VERB_EDIT_TILE,
566         //SP_VERB_EDIT_UNTILE,
567         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
568         SP_VERB_DIALOG_DISPLAY,
569         SP_VERB_DIALOG_FILL_STROKE,
570         SP_VERB_DIALOG_NAMEDVIEW,
571         SP_VERB_DIALOG_TEXT,
572         SP_VERB_DIALOG_XML_EDITOR,
573         SP_VERB_EDIT_CLONE,
574         SP_VERB_EDIT_COPY,
575         SP_VERB_EDIT_CUT,
576         SP_VERB_EDIT_DUPLICATE,
577         SP_VERB_EDIT_PASTE,
578         SP_VERB_EDIT_REDO,
579         SP_VERB_EDIT_UNDO,
580         SP_VERB_EDIT_UNLINK_CLONE,
581         SP_VERB_FILE_EXPORT,
582         SP_VERB_FILE_IMPORT,
583         SP_VERB_FILE_NEW,
584         SP_VERB_FILE_OPEN,
585         SP_VERB_FILE_PRINT,
586         SP_VERB_FILE_SAVE,
587         SP_VERB_OBJECT_TO_CURVE,
588         SP_VERB_SELECTION_GROUP,
589         SP_VERB_SELECTION_OUTLINE,
590         SP_VERB_SELECTION_UNGROUP,
591         SP_VERB_ZOOM_1_1,
592         SP_VERB_ZOOM_1_2,
593         SP_VERB_ZOOM_2_1,
594         SP_VERB_ZOOM_DRAWING,
595         SP_VERB_ZOOM_IN,
596         SP_VERB_ZOOM_NEXT,
597         SP_VERB_ZOOM_OUT,
598         SP_VERB_ZOOM_PAGE,
599         SP_VERB_ZOOM_PAGE_WIDTH,
600         SP_VERB_ZOOM_PREV,
601         SP_VERB_ZOOM_SELECTION,
602     };
604     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
606     static std::map<SPDesktop*, GtkActionGroup*> groups;
607     GtkActionGroup* mainActions = 0;
608     if ( groups.find(desktop) != groups.end() ) {
609         mainActions = groups[desktop];
610     }
612     if ( !mainActions ) {
613         mainActions = gtk_action_group_new("main");
614         groups[desktop] = mainActions;
615     }
617     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
618         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
619         if ( verb ) {
620             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
621                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
622                 gtk_action_group_add_action( mainActions, act );
623             }
624         }
625     }
627     return mainActions;
631 GtkWidget *
632 sp_tool_toolbox_new()
634     GtkTooltips *tt = gtk_tooltips_new();
635     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
637     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
638     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
640     gtk_widget_set_sensitive(tb, FALSE);
642     GtkWidget *hb = gtk_handle_box_new();
643     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
644     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
645     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
647     gtk_container_add(GTK_CONTAINER(hb), tb);
648     gtk_widget_show(GTK_WIDGET(tb));
650     sigc::connection* conn = new sigc::connection;
651     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
653     return hb;
656 static void
657 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
659     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
660     gtk_widget_queue_resize(child);
663 static void
664 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
666     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
667     gtk_widget_queue_resize(child);
670 GtkWidget *
671 sp_aux_toolbox_new()
673     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
675     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
677     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
679     gtk_widget_set_sensitive(tb, FALSE);
681     GtkWidget *hb = gtk_handle_box_new();
682     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
683     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
684     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
686     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
687     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
689     gtk_container_add(GTK_CONTAINER(hb), tb);
690     gtk_widget_show(GTK_WIDGET(tb));
692     sigc::connection* conn = new sigc::connection;
693     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
695     return hb;
698 //####################################
699 //# Commands Bar
700 //####################################
702 GtkWidget *
703 sp_commands_toolbox_new()
705     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
707     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
709     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
710     gtk_widget_set_sensitive(tb, FALSE);
712     GtkWidget *hb = gtk_handle_box_new();
713     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
714     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
715     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
717     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
718     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
720     gtk_container_add(GTK_CONTAINER(hb), tb);
721     gtk_widget_show(GTK_WIDGET(tb));
723     sigc::connection* conn = new sigc::connection;
724     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
726     return hb;
729 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
730                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
731                                                        gchar const *path, gchar const *data, gdouble def,
732                                                        GtkWidget *focusTarget,
733                                                        GtkWidget *us,
734                                                        GObject *dataKludge,
735                                                        gboolean altx, gchar const *altx_mark,
736                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
737                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
738                                                        void (*callback)(GtkAdjustment *, GObject *),
739                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
741     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
742                                                              lower, upper, step, page, page ) );
743     if (us) {
744         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
745     }
747     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
749     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
750     if ( shortLabel ) {
751         g_object_set( act, "short_label", shortLabel, NULL );
752     }
754     if ( (descrCount > 0) && descrLabels && descrValues ) {
755         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
756     }
758     if ( focusTarget ) {
759         ege_adjustment_action_set_focuswidget( act, focusTarget );
760     }
762     if ( altx && altx_mark ) {
763         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
764     }
766     if ( dataKludge ) {
767         g_object_set_data( dataKludge, data, adj );
768     }
770     // Using a cast just to make sure we pass in the right kind of function pointer
771     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
773     return act;
777 //####################################
778 //# node editing callbacks
779 //####################################
781 /**
782  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
783  */
784 static ShapeEditor *get_current_shape_editor()
786     if (!SP_ACTIVE_DESKTOP) {
787         return NULL;
788     }
790     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
792     if (!SP_IS_NODE_CONTEXT(event_context)) {
793         return NULL;
794     }
796     return SP_NODE_CONTEXT(event_context)->shape_editor;
800 void
801 sp_node_path_edit_add(void)
803     ShapeEditor *shape_editor = get_current_shape_editor();
804     if (shape_editor) shape_editor->add_node();
807 void
808 sp_node_path_edit_delete(void)
810     ShapeEditor *shape_editor = get_current_shape_editor();
811     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
814 void
815 sp_node_path_edit_delete_segment(void)
817     ShapeEditor *shape_editor = get_current_shape_editor();
818     if (shape_editor) shape_editor->delete_segment();
821 void
822 sp_node_path_edit_break(void)
824     ShapeEditor *shape_editor = get_current_shape_editor();
825     if (shape_editor) shape_editor->break_at_nodes();
828 void
829 sp_node_path_edit_join(void)
831     ShapeEditor *shape_editor = get_current_shape_editor();
832     if (shape_editor) shape_editor->join_nodes();
835 void
836 sp_node_path_edit_join_segment(void)
838     ShapeEditor *shape_editor = get_current_shape_editor();
839     if (shape_editor) shape_editor->join_segments();
842 void
843 sp_node_path_edit_toline(void)
845     ShapeEditor *shape_editor = get_current_shape_editor();
846     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
849 void
850 sp_node_path_edit_tocurve(void)
852     ShapeEditor *shape_editor = get_current_shape_editor();
853     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
856 void
857 sp_node_path_edit_cusp(void)
859     ShapeEditor *shape_editor = get_current_shape_editor();
860     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
863 void
864 sp_node_path_edit_smooth(void)
866     ShapeEditor *shape_editor = get_current_shape_editor();
867     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
870 void
871 sp_node_path_edit_symmetrical(void)
873     ShapeEditor *shape_editor = get_current_shape_editor();
874     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
877 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
878     bool show = gtk_toggle_action_get_active( act );
879     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
880     ShapeEditor *shape_editor = get_current_shape_editor();
881     if (shape_editor) shape_editor->show_handles(show);
884 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
885     bool show = gtk_toggle_action_get_active( act );
886     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
887     ShapeEditor *shape_editor = get_current_shape_editor();
888     if (shape_editor) shape_editor->show_helperpath(show);
891 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
892     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
895 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
896     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
899 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
900     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
903 /* is called when the node selection is modified */
904 static void
905 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
907     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
908     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
909     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
910     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
912     // quit if run by the attr_changed listener
913     if (g_object_get_data( tbl, "freeze" )) {
914         return;
915     }
917     // in turn, prevent listener from responding
918     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
920     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
921     SPUnit const *unit = tracker->getActiveUnit();
923     ShapeEditor *shape_editor = get_current_shape_editor();
924     if (shape_editor && shape_editor->has_nodepath()) {
925         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
926         int n_selected = 0;
927         if (nodepath) {
928             n_selected = nodepath->numSelected();
929         }
931         if (n_selected == 0) {
932             gtk_action_set_sensitive(xact, FALSE);
933             gtk_action_set_sensitive(yact, FALSE);
934         } else {
935             gtk_action_set_sensitive(xact, TRUE);
936             gtk_action_set_sensitive(yact, TRUE);
937             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
938             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
940             if (n_selected == 1) {
941                 NR::Point sel_node = nodepath->singleSelectedCoords();
942                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
943                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
944                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
945                 }
946             } else {
947                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
948                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
949                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
950                     /* Note: Currently x and y will always have a value, even if the coordinates of the
951                        selected nodes don't coincide (in this case we use the coordinates of the center
952                        of the bounding box). So the entries are never set to zero. */
953                     // FIXME: Maybe we should clear the entry if several nodes are selected
954                     //        instead of providing a kind of average value
955                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
956                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
957                 }
958             }
959         }
960     } else {
961         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
962         gtk_action_set_sensitive(xact, FALSE);
963         gtk_action_set_sensitive(yact, FALSE);
964     }
966     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
969 static void
970 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
972     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
974     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
975     SPUnit const *unit = tracker->getActiveUnit();
977     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
978         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
979     }
981     // quit if run by the attr_changed listener
982     if (g_object_get_data( tbl, "freeze" )) {
983         return;
984     }
986     // in turn, prevent listener from responding
987     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
989     ShapeEditor *shape_editor = get_current_shape_editor();
990     if (shape_editor && shape_editor->has_nodepath()) {
991         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
992         if (!strcmp(value_name, "x")) {
993             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
994         }
995         if (!strcmp(value_name, "y")) {
996             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
997         }
998     }
1000     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1003 static void
1004 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1006     sp_node_path_value_changed(adj, tbl, "x");
1009 static void
1010 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1012     sp_node_path_value_changed(adj, tbl, "y");
1015 void
1016 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1018     {
1019     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1020     SPItem *item = selection->singleItem();
1021     if (item && SP_IS_LPE_ITEM(item)) {
1022        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1023            gtk_action_set_sensitive(w, TRUE);
1024        } else {
1025            gtk_action_set_sensitive(w, FALSE);
1026        }
1027     } else {
1028        gtk_action_set_sensitive(w, FALSE);
1029     }
1030     }
1032     {
1033     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1034     SPItem *item = selection->singleItem();
1035     if (item && item->clip_ref && item->clip_ref->getObject()) {
1036        gtk_action_set_sensitive(w, TRUE);
1037     } else {
1038        gtk_action_set_sensitive(w, FALSE);
1039     }
1040     }
1042     {
1043     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1044     SPItem *item = selection->singleItem();
1045     if (item && item->mask_ref && item->mask_ref->getObject()) {
1046        gtk_action_set_sensitive(w, TRUE);
1047     } else {
1048        gtk_action_set_sensitive(w, FALSE);
1049     }
1050     }
1053 void
1054 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1056     sp_node_toolbox_sel_changed (selection, tbl);
1061 //################################
1062 //##    Node Editing Toolbox    ##
1063 //################################
1065 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1067     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1068     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1069     g_object_set_data( holder, "tracker", tracker );
1071     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1073     {
1074         InkAction* inky = ink_action_new( "NodeInsertAction",
1075                                           _("Insert node"),
1076                                           _("Insert new nodes into selected segments"),
1077                                           "node_insert",
1078                                           secondarySize );
1079         g_object_set( inky, "short_label", _("Insert"), NULL );
1080         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1081         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1082     }
1084     {
1085         InkAction* inky = ink_action_new( "NodeDeleteAction",
1086                                           _("Delete node"),
1087                                           _("Delete selected nodes"),
1088                                           "node_delete",
1089                                           secondarySize );
1090         g_object_set( inky, "short_label", _("Delete"), NULL );
1091         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1092         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1093     }
1095     {
1096         InkAction* inky = ink_action_new( "NodeJoinAction",
1097                                           _("Join endnodes"),
1098                                           _("Join selected endnodes"),
1099                                           "node_join",
1100                                           secondarySize );
1101         g_object_set( inky, "short_label", _("Join"), NULL );
1102         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1103         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1104     }
1106     {
1107         InkAction* inky = ink_action_new( "NodeBreakAction",
1108                                           _("Break nodes"),
1109                                           _("Break path at selected nodes"),
1110                                           "node_break",
1111                                           secondarySize );
1112         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1113         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1114     }
1117     {
1118         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1119                                           _("Join with segment"),
1120                                           _("Join selected endnodes with a new segment"),
1121                                           "node_join_segment",
1122                                           secondarySize );
1123         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1124         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1125     }
1127     {
1128         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1129                                           _("Delete segment"),
1130                                           _("Delete segment between two non-endpoint nodes"),
1131                                           "node_delete_segment",
1132                                           secondarySize );
1133         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1134         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1135     }
1137     {
1138         InkAction* inky = ink_action_new( "NodeCuspAction",
1139                                           _("Node Cusp"),
1140                                           _("Make selected nodes corner"),
1141                                           "node_cusp",
1142                                           secondarySize );
1143         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1144         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1145     }
1147     {
1148         InkAction* inky = ink_action_new( "NodeSmoothAction",
1149                                           _("Node Smooth"),
1150                                           _("Make selected nodes smooth"),
1151                                           "node_smooth",
1152                                           secondarySize );
1153         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1154         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1155     }
1157     {
1158         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1159                                           _("Node Symmetric"),
1160                                           _("Make selected nodes symmetric"),
1161                                           "node_symmetric",
1162                                           secondarySize );
1163         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1164         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1165     }
1167     {
1168         InkAction* inky = ink_action_new( "NodeLineAction",
1169                                           _("Node Line"),
1170                                           _("Make selected segments lines"),
1171                                           "node_line",
1172                                           secondarySize );
1173         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1174         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1175     }
1177     {
1178         InkAction* inky = ink_action_new( "NodeCurveAction",
1179                                           _("Node Curve"),
1180                                           _("Make selected segments curves"),
1181                                           "node_curve",
1182                                           secondarySize );
1183         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1184         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1185     }
1187     {
1188         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1189                                                       _("Show Handles"),
1190                                                       _("Show the Bezier handles of selected nodes"),
1191                                                       "nodes_show_handles",
1192                                                       Inkscape::ICON_SIZE_DECORATION );
1193         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1194         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1195         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1196     }
1198     {
1199         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1200                                                       _("Show Outline"),
1201                                                       _("Show the outline of the path"),
1202                                                       "nodes_show_helperpath",
1203                                                       Inkscape::ICON_SIZE_DECORATION );
1204         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1205         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1206         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1207     }
1209     {
1210         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1211                                           _("Next path effect parameter"),
1212                                           _("Show next path effect parameter for editing"),
1213                                           "edit_next_parameter",
1214                                           Inkscape::ICON_SIZE_DECORATION );
1215         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1216         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1217         g_object_set_data( holder, "nodes_lpeedit", inky);
1218     }
1220     {
1221         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1222                                           _("Edit clipping path"),
1223                                           _("Edit the clipping path of the object"),
1224                                           "nodeedit-clippath",
1225                                           Inkscape::ICON_SIZE_DECORATION );
1226         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1227         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1228         g_object_set_data( holder, "nodes_clippathedit", inky);
1229     }
1231     {
1232         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1233                                           _("Edit mask path"),
1234                                           _("Edit the mask of the object"),
1235                                           "nodeedit-mask",
1236                                           Inkscape::ICON_SIZE_DECORATION );
1237         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1238         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1239         g_object_set_data( holder, "nodes_maskedit", inky);
1240     }
1242     /* X coord of selected node(s) */
1243     {
1244         EgeAdjustmentAction* eact = 0;
1245         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1246         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1247         eact = create_adjustment_action( "NodeXAction",
1248                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1249                                          "tools.nodes", "Xcoord", 0,
1250                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1251                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1252                                          labels, values, G_N_ELEMENTS(labels),
1253                                          sp_node_path_x_value_changed );
1254         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1255         g_object_set_data( holder, "nodes_x_action", eact );
1256         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1257         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1258     }
1260     /* Y coord of selected node(s) */
1261     {
1262         EgeAdjustmentAction* eact = 0;
1263         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1264         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1265         eact = create_adjustment_action( "NodeYAction",
1266                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1267                                          "tools.nodes", "Ycoord", 0,
1268                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1269                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1270                                          labels, values, G_N_ELEMENTS(labels),
1271                                          sp_node_path_y_value_changed );
1272         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1273         g_object_set_data( holder, "nodes_y_action", eact );
1274         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1275         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1276     }
1278     // add the units menu
1279     {
1280         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1281         gtk_action_group_add_action( mainActions, act );
1282     }
1285     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1287     //watch selection
1288     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1290     sigc::connection *c_selection_changed =
1291         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1292                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1293     pool->add_connection ("selection-changed", c_selection_changed);
1295     sigc::connection *c_selection_modified =
1296         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1297                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1298     pool->add_connection ("selection-modified", c_selection_modified);
1300     sigc::connection *c_subselection_changed =
1301         new sigc::connection (desktop->connectToolSubselectionChanged
1302                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1303     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1305     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1307     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1308 } // end of sp_node_toolbox_prep()
1311 //########################
1312 //##    Zoom Toolbox    ##
1313 //########################
1315 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1317     // no custom GtkAction setup needed
1318 } // end of sp_zoom_toolbox_prep()
1320 void
1321 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1323     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")));
1327 void
1328 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1330     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")));
1333 void
1334 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1336     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")));
1339 static void
1340 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1342     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1343     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1345     if (old_desktop) {
1346         GList *children, *iter;
1348         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1349         for ( iter = children ; iter ; iter = iter->next ) {
1350             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1351         }
1352         g_list_free(children);
1353     }
1355     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1357     if (desktop) {
1358         gtk_widget_set_sensitive(toolbox, TRUE);
1359         setup_func(toolbox, desktop);
1360         update_func(desktop, desktop->event_context, toolbox);
1361         *conn = desktop->connectEventContextChanged
1362             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1363     } else {
1364         gtk_widget_set_sensitive(toolbox, FALSE);
1365     }
1367 } // end of toolbox_set_desktop()
1370 static void
1371 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1373     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1374     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1376     for (int i = 0 ; tools[i].type_name ; i++ ) {
1377         GtkWidget *button =
1378             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1379                                                               SP_BUTTON_TYPE_TOGGLE,
1380                                                               Inkscape::Verb::get(tools[i].verb),
1381                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1382                                                               desktop,
1383                                                               tooltips );
1385         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1386                            (gpointer)button );
1387     }
1391 static void
1392 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1394     gchar const *const tname = ( eventcontext
1395                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1396                                  : NULL );
1397     for (int i = 0 ; tools[i].type_name ; i++ ) {
1398         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1399         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1400     }
1403 static void
1404 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1406     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1407     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1408     GtkUIManager* mgr = gtk_ui_manager_new();
1409     GError* errVal = 0;
1410     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1411     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1413     std::map<std::string, GtkWidget*> dataHolders;
1415     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1416         if ( aux_toolboxes[i].prep_func ) {
1417             // converted to GtkActions and UIManager
1419             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1420             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1421             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1422             dataHolders[aux_toolboxes[i].type_name] = kludge;
1423             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1424         } else {
1426             GtkWidget *sub_toolbox = 0;
1427             if (aux_toolboxes[i].create_func == NULL)
1428                 sub_toolbox = sp_empty_toolbox_new(desktop);
1429             else {
1430                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1431             }
1433             gtk_size_group_add_widget( grouper, sub_toolbox );
1435             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1436             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1438         }
1439     }
1441     // Second pass to create toolbars *after* all GtkActions are created
1442     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1443         if ( aux_toolboxes[i].prep_func ) {
1444             // converted to GtkActions and UIManager
1446             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1448             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1449             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1451             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1452             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1453             g_free( tmp );
1454             tmp = 0;
1456             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1457             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1458                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1459             }
1460             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1463             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1465             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1466                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1467                 swatch->setDesktop( desktop );
1468                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1469                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1470                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1471                 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 );
1472             }
1474             gtk_widget_show_all( holder );
1475             sp_set_font_size_smaller( holder );
1477             gtk_size_group_add_widget( grouper, holder );
1479             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1480             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1481         }
1482     }
1484     g_object_unref( G_OBJECT(grouper) );
1487 static void
1488 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1490     gchar const *tname = ( eventcontext
1491                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1492                            : NULL );
1493     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1494         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1495         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1496             gtk_widget_show_all(sub_toolbox);
1497             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1498         } else {
1499             gtk_widget_hide(sub_toolbox);
1500         }
1501     }
1504 static void
1505 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1507     gchar const * descr =
1508         "<ui>"
1509         "  <toolbar name='CommandsToolbar'>"
1510         "    <toolitem action='FileNew' />"
1511         "    <toolitem action='FileOpen' />"
1512         "    <toolitem action='FileSave' />"
1513         "    <toolitem action='FilePrint' />"
1514         "    <separator />"
1515         "    <toolitem action='FileImport' />"
1516         "    <toolitem action='FileExport' />"
1517         "    <separator />"
1518         "    <toolitem action='EditUndo' />"
1519         "    <toolitem action='EditRedo' />"
1520         "    <separator />"
1521         "    <toolitem action='EditCopy' />"
1522         "    <toolitem action='EditCut' />"
1523         "    <toolitem action='EditPaste' />"
1524         "    <separator />"
1525         "    <toolitem action='ZoomSelection' />"
1526         "    <toolitem action='ZoomDrawing' />"
1527         "    <toolitem action='ZoomPage' />"
1528         "    <separator />"
1529         "    <toolitem action='EditDuplicate' />"
1530         "    <toolitem action='EditClone' />"
1531         "    <toolitem action='EditUnlinkClone' />"
1532         "    <separator />"
1533         "    <toolitem action='SelectionGroup' />"
1534         "    <toolitem action='SelectionUnGroup' />"
1535         "    <separator />"
1536         "    <toolitem action='DialogFillStroke' />"
1537         "    <toolitem action='DialogText' />"
1538         "    <toolitem action='DialogXMLEditor' />"
1539         "    <toolitem action='DialogAlignDistribute' />"
1540         "    <separator />"
1541         "    <toolitem action='DialogPreferences' />"
1542         "    <toolitem action='DialogDocumentProperties' />"
1543         "  </toolbar>"
1544         "</ui>";
1545     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1548     GtkUIManager* mgr = gtk_ui_manager_new();
1549     GError* errVal = 0;
1551     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1552     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1554     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1555     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1556         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1557     }
1559     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1560     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1563     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1566 static void
1567 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1571 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1573     gtk_widget_show(toolbox_toplevel);
1574     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1576     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1577     if (!shown_toolbox) {
1578         return;
1579     }
1580     gtk_widget_show(toolbox);
1582     gtk_widget_show_all(shown_toolbox);
1585 void
1586 aux_toolbox_space(GtkWidget *tb, gint space)
1588     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1591 static GtkWidget *
1592 sp_empty_toolbox_new(SPDesktop *desktop)
1594     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1595     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1596     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1598     gtk_widget_show_all(tbl);
1599     sp_set_font_size_smaller (tbl);
1601     return tbl;
1604 // helper UI functions
1606 GtkWidget *
1607 sp_tb_spinbutton(
1608     gchar *label, gchar const *tooltip,
1609     gchar const *path, gchar const *data, gdouble def,
1610     GtkWidget *us,
1611     GtkWidget *tbl,
1612     gboolean altx, gchar const *altx_mark,
1613     gdouble lower, gdouble upper, gdouble step, gdouble page,
1614     void (*callback)(GtkAdjustment *, GtkWidget *),
1615     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1617     GtkTooltips *tt = gtk_tooltips_new();
1619     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1621     GtkWidget *l = gtk_label_new(label);
1622     gtk_widget_show(l);
1623     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1624     gtk_container_add(GTK_CONTAINER(hb), l);
1626     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1627                                       lower, upper, step, page, page);
1628     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1629     if (us)
1630         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1632     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1633     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1634     if (altx)
1635         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1636     gtk_widget_set_size_request(sb,
1637                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1638                                 AUX_SPINBUTTON_HEIGHT);
1639     gtk_widget_show(sb);
1640     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1641     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1642     gtk_container_add(GTK_CONTAINER(hb), sb);
1643     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1645     return hb;
1648 #define MODE_LABEL_WIDTH 70
1650 //########################
1651 //##       Star         ##
1652 //########################
1654 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1656     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1658     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1659         // do not remember prefs if this call is initiated by an undo change, because undoing object
1660         // creation sets bogus values to its attributes before it is deleted
1661         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1662     }
1664     // quit if run by the attr_changed listener
1665     if (g_object_get_data( dataKludge, "freeze" )) {
1666         return;
1667     }
1669     // in turn, prevent listener from responding
1670     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1672     bool modmade = false;
1674     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1675     GSList const *items = selection->itemList();
1676     for (; items != NULL; items = items->next) {
1677         if (SP_IS_STAR((SPItem *) items->data)) {
1678             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1679             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1680             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1681                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1682                                     + M_PI / (gint)adj->value));
1683             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1684             modmade = true;
1685         }
1686     }
1687     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1688                                    _("Star: Change number of corners"));
1690     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1693 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1695     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1697     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1698         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1699     }
1701     // quit if run by the attr_changed listener
1702     if (g_object_get_data( dataKludge, "freeze" )) {
1703         return;
1704     }
1706     // in turn, prevent listener from responding
1707     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1709     bool modmade = false;
1710     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1711     GSList const *items = selection->itemList();
1712     for (; items != NULL; items = items->next) {
1713         if (SP_IS_STAR((SPItem *) items->data)) {
1714             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1716             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1717             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1718             if (r2 < r1) {
1719                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1720             } else {
1721                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1722             }
1724             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1725             modmade = true;
1726         }
1727     }
1729     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1730                                    _("Star: Change spoke ratio"));
1732     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1735 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1737     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1738     bool flat = ege_select_one_action_get_active( act ) == 0;
1740     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1741         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1742                                     flat ? "true" : "false" );
1743     }
1745     // quit if run by the attr_changed listener
1746     if (g_object_get_data( dataKludge, "freeze" )) {
1747         return;
1748     }
1750     // in turn, prevent listener from responding
1751     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1753     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1754     GSList const *items = selection->itemList();
1755     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1756     bool modmade = false;
1758     if ( prop_action ) {
1759         gtk_action_set_sensitive( prop_action, !flat );
1760     }
1762     for (; items != NULL; items = items->next) {
1763         if (SP_IS_STAR((SPItem *) items->data)) {
1764             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1765             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1766             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1767             modmade = true;
1768         }
1769     }
1771     if (modmade) {
1772         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1773                          flat ? _("Make polygon") : _("Make star"));
1774     }
1776     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1779 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1781     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1783     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1784         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1785     }
1787     // quit if run by the attr_changed listener
1788     if (g_object_get_data( dataKludge, "freeze" )) {
1789         return;
1790     }
1792     // in turn, prevent listener from responding
1793     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1795     bool modmade = false;
1797     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1798     GSList const *items = selection->itemList();
1799     for (; items != NULL; items = items->next) {
1800         if (SP_IS_STAR((SPItem *) items->data)) {
1801             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1802             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1803             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1804             modmade = true;
1805         }
1806     }
1807     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1808                                    _("Star: Change rounding"));
1810     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1813 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1815     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1817     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1818         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1819     }
1821     // quit if run by the attr_changed listener
1822     if (g_object_get_data( dataKludge, "freeze" )) {
1823         return;
1824     }
1826     // in turn, prevent listener from responding
1827     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1829     bool modmade = false;
1831     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1832     GSList const *items = selection->itemList();
1833     for (; items != NULL; items = items->next) {
1834         if (SP_IS_STAR((SPItem *) items->data)) {
1835             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1836             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1837             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1838             modmade = true;
1839         }
1840     }
1841     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1842                                    _("Star: Change randomization"));
1844     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1848 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1849                                        gchar const */*old_value*/, gchar const */*new_value*/,
1850                                        bool /*is_interactive*/, gpointer data)
1852     GtkWidget *tbl = GTK_WIDGET(data);
1854     // quit if run by the _changed callbacks
1855     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1856         return;
1857     }
1859     // in turn, prevent callbacks from responding
1860     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1862     GtkAdjustment *adj = 0;
1864     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1865     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1867     if (!strcmp(name, "inkscape:randomized")) {
1868         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1869         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1870     } else if (!strcmp(name, "inkscape:rounded")) {
1871         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1872         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1873     } else if (!strcmp(name, "inkscape:flatsided")) {
1874         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1875         char const *flatsides = repr->attribute("inkscape:flatsided");
1876         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1877         if ( flatsides && !strcmp(flatsides,"false") ) {
1878             ege_select_one_action_set_active( flat_action, 1 );
1879             gtk_action_set_sensitive( prop_action, TRUE );
1880         } else {
1881             ege_select_one_action_set_active( flat_action, 0 );
1882             gtk_action_set_sensitive( prop_action, FALSE );
1883         }
1884     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1885         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1886         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1887         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1888         if (r2 < r1) {
1889             gtk_adjustment_set_value(adj, r2/r1);
1890         } else {
1891             gtk_adjustment_set_value(adj, r1/r2);
1892         }
1893     } else if (!strcmp(name, "sodipodi:sides")) {
1894         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1895         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1896     }
1898     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1902 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1904     NULL, /* child_added */
1905     NULL, /* child_removed */
1906     star_tb_event_attr_changed,
1907     NULL, /* content_changed */
1908     NULL  /* order_changed */
1909 };
1912 /**
1913  *  \param selection Should not be NULL.
1914  */
1915 static void
1916 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1918     int n_selected = 0;
1919     Inkscape::XML::Node *repr = NULL;
1921     purge_repr_listener( tbl, tbl );
1923     for (GSList const *items = selection->itemList();
1924          items != NULL;
1925          items = items->next)
1926     {
1927         if (SP_IS_STAR((SPItem *) items->data)) {
1928             n_selected++;
1929             repr = SP_OBJECT_REPR((SPItem *) items->data);
1930         }
1931     }
1933     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1935     if (n_selected == 0) {
1936         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1937     } else if (n_selected == 1) {
1938         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1940         if (repr) {
1941             g_object_set_data( tbl, "repr", repr );
1942             Inkscape::GC::anchor(repr);
1943             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1944             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1945         }
1946     } else {
1947         // FIXME: implement averaging of all parameters for multiple selected stars
1948         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1949         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1950     }
1954 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1956     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1957     // callbacks to lump all the changes for all selected objects in one undo step
1959     GtkAdjustment *adj = 0;
1961     // fixme: make settable in prefs!
1962     gint mag = 5;
1963     gdouble prop = 0.5;
1964     gboolean flat = FALSE;
1965     gdouble randomized = 0;
1966     gdouble rounded = 0;
1968     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1969     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1971     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1972     gtk_action_set_sensitive( sb2, !flat );
1974     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1975     gtk_adjustment_set_value(adj, mag);
1976     gtk_adjustment_value_changed(adj);
1978     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1979     gtk_adjustment_set_value(adj, prop);
1980     gtk_adjustment_value_changed(adj);
1982     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1983     gtk_adjustment_set_value(adj, rounded);
1984     gtk_adjustment_value_changed(adj);
1986     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1987     gtk_adjustment_set_value(adj, randomized);
1988     gtk_adjustment_value_changed(adj);
1992 void
1993 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1995     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1996     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1997     GtkWidget *l = gtk_label_new(NULL);
1998     gtk_label_set_markup(GTK_LABEL(l), title);
1999     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2000     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2001     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2005 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2007     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2009     {
2010         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2011         ege_output_action_set_use_markup( act, TRUE );
2012         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2013         g_object_set_data( holder, "mode_action", act );
2014     }
2016     {
2017         EgeAdjustmentAction* eact = 0;
2018         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2019         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2021         /* Flatsided checkbox */
2022         {
2023             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2025             GtkTreeIter iter;
2026             gtk_list_store_append( model, &iter );
2027             gtk_list_store_set( model, &iter,
2028                                 0, _("Polygon"),
2029                                 1, _("Regular polygon (with one handle) instead of a star"),
2030                                 2, "star_flat",
2031                                 -1 );
2033             gtk_list_store_append( model, &iter );
2034             gtk_list_store_set( model, &iter,
2035                                 0, _("Star"),
2036                                 1, _("Star instead of a regular polygon (with one handle)"),
2037                                 2, "star_angled",
2038                                 -1 );
2040             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2041             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2042             g_object_set_data( holder, "flat_action", act );
2044             ege_select_one_action_set_appearance( act, "full" );
2045             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2046             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2047             ege_select_one_action_set_icon_column( act, 2 );
2048             ege_select_one_action_set_icon_size( act, secondarySize );
2049             ege_select_one_action_set_tooltip_column( act, 1  );
2051             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2052             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2053         }
2055         /* Magnitude */
2056         {
2057         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2058         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2059         eact = create_adjustment_action( "MagnitudeAction",
2060                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2061                                          "tools.shapes.star", "magnitude", 3,
2062                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2063                                          3, 1024, 1, 5,
2064                                          labels, values, G_N_ELEMENTS(labels),
2065                                          sp_stb_magnitude_value_changed,
2066                                          1.0, 0 );
2067         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2068         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2069         }
2071         /* Spoke ratio */
2072         {
2073         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2074         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2075         eact = create_adjustment_action( "SpokeAction",
2076                                          _("Spoke ratio"), _("Spoke ratio:"),
2077                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2078                                          // Base radius is the same for the closest handle.
2079                                          _("Base radius to tip radius ratio"),
2080                                          "tools.shapes.star", "proportion", 0.5,
2081                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2082                                          0.01, 1.0, 0.01, 0.1,
2083                                          labels, values, G_N_ELEMENTS(labels),
2084                                          sp_stb_proportion_value_changed );
2085         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2086         g_object_set_data( holder, "prop_action", eact );
2087         }
2089         if ( !isFlatSided ) {
2090             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2091         } else {
2092             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2093         }
2095         /* Roundedness */
2096         {
2097         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2098         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2099         eact = create_adjustment_action( "RoundednessAction",
2100                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2101                                          "tools.shapes.star", "rounded", 0.0,
2102                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2103                                          -10.0, 10.0, 0.01, 0.1,
2104                                          labels, values, G_N_ELEMENTS(labels),
2105                                          sp_stb_rounded_value_changed );
2106         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2107         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2108         }
2110         /* Randomization */
2111         {
2112         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2113         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2114         eact = create_adjustment_action( "RandomizationAction",
2115                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2116                                          "tools.shapes.star", "randomized", 0.0,
2117                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2118                                          -10.0, 10.0, 0.001, 0.01,
2119                                          labels, values, G_N_ELEMENTS(labels),
2120                                          sp_stb_randomized_value_changed, 0.1, 3 );
2121         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2122         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2123         }
2124     }
2126     {
2127         /* Reset */
2128         {
2129             GtkAction* act = gtk_action_new( "StarResetAction",
2130                                              _("Defaults"),
2131                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2132                                              GTK_STOCK_CLEAR );
2133             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2134             gtk_action_group_add_action( mainActions, act );
2135             gtk_action_set_sensitive( act, TRUE );
2136         }
2137     }
2139     sigc::connection *connection = new sigc::connection(
2140         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2141         );
2142     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2143     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2147 //########################
2148 //##       Rect         ##
2149 //########################
2151 static void sp_rtb_sensitivize( GObject *tbl )
2153     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2154     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2155     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2157     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2158         gtk_action_set_sensitive( not_rounded, FALSE );
2159     } else {
2160         gtk_action_set_sensitive( not_rounded, TRUE );
2161     }
2165 static void
2166 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2167                           void (*setter)(SPRect *, gdouble))
2169     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2171     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2172     SPUnit const *unit = tracker->getActiveUnit();
2174     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2175         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2176     }
2178     // quit if run by the attr_changed listener
2179     if (g_object_get_data( tbl, "freeze" )) {
2180         return;
2181     }
2183     // in turn, prevent listener from responding
2184     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2186     bool modmade = false;
2187     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2188     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2189         if (SP_IS_RECT(items->data)) {
2190             if (adj->value != 0) {
2191                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2192             } else {
2193                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2194             }
2195             modmade = true;
2196         }
2197     }
2199     sp_rtb_sensitivize( tbl );
2201     if (modmade) {
2202         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2203                                    _("Change rectangle"));
2204     }
2206     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2209 static void
2210 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2212     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2215 static void
2216 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2218     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2221 static void
2222 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2224     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2227 static void
2228 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2230     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2235 static void
2236 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2238     GtkAdjustment *adj = 0;
2240     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2241     gtk_adjustment_set_value(adj, 0.0);
2242     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2243     gtk_adjustment_value_changed(adj);
2245     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2246     gtk_adjustment_set_value(adj, 0.0);
2247     gtk_adjustment_value_changed(adj);
2249     sp_rtb_sensitivize( obj );
2252 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2253                                        gchar const */*old_value*/, gchar const */*new_value*/,
2254                                        bool /*is_interactive*/, gpointer data)
2256     GObject *tbl = G_OBJECT(data);
2258     // quit if run by the _changed callbacks
2259     if (g_object_get_data( tbl, "freeze" )) {
2260         return;
2261     }
2263     // in turn, prevent callbacks from responding
2264     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2266     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2267     SPUnit const *unit = tracker->getActiveUnit();
2269     gpointer item = g_object_get_data( tbl, "item" );
2270     if (item && SP_IS_RECT(item)) {
2271         {
2272             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2273             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2274             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2275         }
2277         {
2278             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2279             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2280             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2281         }
2283         {
2284             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2285             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2286             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2287         }
2289         {
2290             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2291             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2292             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2293         }
2294     }
2296     sp_rtb_sensitivize( tbl );
2298     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2302 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2303     NULL, /* child_added */
2304     NULL, /* child_removed */
2305     rect_tb_event_attr_changed,
2306     NULL, /* content_changed */
2307     NULL  /* order_changed */
2308 };
2310 /**
2311  *  \param selection should not be NULL.
2312  */
2313 static void
2314 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2316     int n_selected = 0;
2317     Inkscape::XML::Node *repr = NULL;
2318     SPItem *item = NULL;
2320     if ( g_object_get_data( tbl, "repr" ) ) {
2321         g_object_set_data( tbl, "item", NULL );
2322     }
2323     purge_repr_listener( tbl, tbl );
2325     for (GSList const *items = selection->itemList();
2326          items != NULL;
2327          items = items->next) {
2328         if (SP_IS_RECT((SPItem *) items->data)) {
2329             n_selected++;
2330             item = (SPItem *) items->data;
2331             repr = SP_OBJECT_REPR(item);
2332         }
2333     }
2335     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2337     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2339     if (n_selected == 0) {
2340         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2342         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2343         gtk_action_set_sensitive(w, FALSE);
2344         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2345         gtk_action_set_sensitive(h, FALSE);
2347     } else if (n_selected == 1) {
2348         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2349         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2351         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2352         gtk_action_set_sensitive(w, TRUE);
2353         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2354         gtk_action_set_sensitive(h, TRUE);
2356         if (repr) {
2357             g_object_set_data( tbl, "repr", repr );
2358             g_object_set_data( tbl, "item", item );
2359             Inkscape::GC::anchor(repr);
2360             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2361             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2362         }
2363     } else {
2364         // FIXME: implement averaging of all parameters for multiple selected
2365         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2366         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2367         sp_rtb_sensitivize( tbl );
2368     }
2372 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2374     EgeAdjustmentAction* eact = 0;
2375     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2377     {
2378         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2379         ege_output_action_set_use_markup( act, TRUE );
2380         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2381         g_object_set_data( holder, "mode_action", act );
2382     }
2384     // rx/ry units menu: create
2385     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2386     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2387     // fixme: add % meaning per cent of the width/height
2388     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2389     g_object_set_data( holder, "tracker", tracker );
2391     /* W */
2392     {
2393         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2394         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2395         eact = create_adjustment_action( "RectWidthAction",
2396                                          _("Width"), _("W:"), _("Width of rectangle"),
2397                                          "tools.shapes.rect", "width", 0,
2398                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2399                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2400                                          labels, values, G_N_ELEMENTS(labels),
2401                                          sp_rtb_width_value_changed );
2402         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2403         g_object_set_data( holder, "width_action", eact );
2404         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2405         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2406     }
2408     /* H */
2409     {
2410         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2411         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2412         eact = create_adjustment_action( "RectHeightAction",
2413                                          _("Height"), _("H:"), _("Height of rectangle"),
2414                                          "tools.shapes.rect", "height", 0,
2415                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2416                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2417                                          labels, values, G_N_ELEMENTS(labels),
2418                                          sp_rtb_height_value_changed );
2419         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2420         g_object_set_data( holder, "height_action", eact );
2421         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2422         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2423     }
2425     /* rx */
2426     {
2427         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2428         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2429         eact = create_adjustment_action( "RadiusXAction",
2430                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2431                                          "tools.shapes.rect", "rx", 0,
2432                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2433                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2434                                          labels, values, G_N_ELEMENTS(labels),
2435                                          sp_rtb_rx_value_changed);
2436         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2437         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2438     }
2440     /* ry */
2441     {
2442         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2443         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2444         eact = create_adjustment_action( "RadiusYAction",
2445                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2446                                          "tools.shapes.rect", "ry", 0,
2447                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2448                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2449                                          labels, values, G_N_ELEMENTS(labels),
2450                                          sp_rtb_ry_value_changed);
2451         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2452         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2453     }
2455     // add the units menu
2456     {
2457         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2458         gtk_action_group_add_action( mainActions, act );
2459     }
2461     /* Reset */
2462     {
2463         InkAction* inky = ink_action_new( "RectResetAction",
2464                                           _("Not rounded"),
2465                                           _("Make corners sharp"),
2466                                           "squared_corner",
2467                                           secondarySize );
2468         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2469         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2470         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2471         g_object_set_data( holder, "not_rounded", inky );
2472     }
2474     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2475     sp_rtb_sensitivize( holder );
2477     sigc::connection *connection = new sigc::connection(
2478         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2479         );
2480     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2481     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2484 //########################
2485 //##       3D Box       ##
2486 //########################
2488 // normalize angle so that it lies in the interval [0,360]
2489 static double box3d_normalize_angle (double a) {
2490     double angle = a + ((int) (a/360.0))*360;
2491     if (angle < 0) {
2492         angle += 360.0;
2493     }
2494     return angle;
2497 static void
2498 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2499                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2500     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2501     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2502     //       are reset).
2503     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2505     if (is_infinite) {
2506         gtk_toggle_action_set_active(tact, TRUE);
2507         gtk_action_set_sensitive(act, TRUE);
2509         double angle = persp3d_get_infinite_angle(persp, axis);
2510         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2511             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2512         }
2513     } else {
2514         gtk_toggle_action_set_active(tact, FALSE);
2515         gtk_action_set_sensitive(act, FALSE);
2516     }
2519 static void
2520 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2521     if (!persp_repr) {
2522         g_print ("No perspective given to box3d_resync_toolbar().\n");
2523         return;
2524     }
2526     GtkWidget *tbl = GTK_WIDGET(data);
2527     GtkAdjustment *adj = 0;
2528     GtkAction *act = 0;
2529     GtkToggleAction *tact = 0;
2530     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2531     {
2532         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2533         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2534         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2536         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2537     }
2538     {
2539         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2540         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2541         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2543         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2544     }
2545     {
2546         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2547         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2548         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2550         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2551     }
2554 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2555                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2556                                                   bool /*is_interactive*/, gpointer data)
2558     GtkWidget *tbl = GTK_WIDGET(data);
2560     // quit if run by the attr_changed listener
2561     // note: it used to work without the differently called freeze_ attributes (here and in
2562     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2563     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2564         return;
2565     }
2567     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2568     // sp_document_maybe_done() when the document is undo insensitive)
2569     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2571     // TODO: Only update the appropriate part of the toolbar
2572 //    if (!strcmp(name, "inkscape:vp_z")) {
2573         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2574 //    }
2576     Persp3D *persp = persp3d_get_from_repr(repr);
2577     persp3d_update_box_reprs(persp);
2579     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2582 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2584     NULL, /* child_added */
2585     NULL, /* child_removed */
2586     box3d_persp_tb_event_attr_changed,
2587     NULL, /* content_changed */
2588     NULL  /* order_changed */
2589 };
2591 /**
2592  *  \param selection Should not be NULL.
2593  */
2594 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2595 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2596 static void
2597 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2599     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2600     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2601     // update the perspectives with infinite VPs and leave the other ones untouched).
2603     Inkscape::XML::Node *persp_repr = NULL;
2604     purge_repr_listener(tbl, tbl);
2606     SPItem *item = selection->singleItem();
2607     if (item && SP_IS_BOX3D(item)) {
2608         // FIXME: Also deal with multiple selected boxes
2609         SPBox3D *box = SP_BOX3D(item);
2610         Persp3D *persp = box3d_get_perspective(box);
2611         persp_repr = SP_OBJECT_REPR(persp);
2612         if (persp_repr) {
2613             g_object_set_data(tbl, "repr", persp_repr);
2614             Inkscape::GC::anchor(persp_repr);
2615             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2616             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2617         }
2619         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2620         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2622         box3d_resync_toolbar(persp_repr, tbl);
2623     }
2626 static void
2627 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2629     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2630     SPDocument *document = sp_desktop_document(desktop);
2632     // quit if run by the attr_changed listener
2633     // note: it used to work without the differently called freeze_ attributes (here and in
2634     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2635     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2636         return;
2637     }
2639     // in turn, prevent listener from responding
2640     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2642     //Persp3D *persp = document->current_persp3d;
2643     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2644     if (sel_persps.empty()) {
2645         // this can happen when the document is created; we silently ignore it
2646         return;
2647     }
2648     Persp3D *persp = sel_persps.front();
2650     persp->tmat.set_infinite_direction (axis, adj->value);
2651     SP_OBJECT(persp)->updateRepr();
2653     // TODO: use the correct axis here, too
2654     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2656     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2660 static void
2661 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2663     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2666 static void
2667 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2669     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2672 static void
2673 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2675     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2679 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2681     // TODO: Take all selected perspectives into account
2682     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2683     if (sel_persps.empty()) {
2684         // this can happen when the document is created; we silently ignore it
2685         return;
2686     }
2687     Persp3D *persp = sel_persps.front();
2689     bool set_infinite = gtk_toggle_action_get_active(act);
2690     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2693 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2695     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2698 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2700     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2703 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2705     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2708 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2710     EgeAdjustmentAction* eact = 0;
2711     SPDocument *document = sp_desktop_document (desktop);
2712     Persp3D *persp = document->current_persp3d;
2714     EgeAdjustmentAction* box3d_angle_x = 0;
2715     EgeAdjustmentAction* box3d_angle_y = 0;
2716     EgeAdjustmentAction* box3d_angle_z = 0;
2718     /* Angle X */
2719     {
2720         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2721         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2722         eact = create_adjustment_action( "3DBoxAngleXAction",
2723                                          _("Angle in X direction"), _("Angle X:"),
2724                                          // Translators: PL is short for 'perspective line'
2725                                          _("Angle of PLs in X direction"),
2726                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2727                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2728                                          -360.0, 360.0, 1.0, 10.0,
2729                                          labels, values, G_N_ELEMENTS(labels),
2730                                          box3d_angle_x_value_changed );
2731         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2732         g_object_set_data( holder, "box3d_angle_x_action", eact );
2733         box3d_angle_x = eact;
2734     }
2736     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2737         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2738     } else {
2739         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2740     }
2743     /* VP X state */
2744     {
2745         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2746                                                       // Translators: VP is short for 'vanishing point'
2747                                                       _("State of VP in X direction"),
2748                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2749                                                       "toggle_vp_x",
2750                                                       Inkscape::ICON_SIZE_DECORATION );
2751         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2752         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2753         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2754         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2755         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2756     }
2758     /* Angle Y */
2759     {
2760         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2761         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2762         eact = create_adjustment_action( "3DBoxAngleYAction",
2763                                          _("Angle in Y direction"), _("Angle Y:"),
2764                                          // Translators: PL is short for 'perspective line'
2765                                          _("Angle of PLs in Y direction"),
2766                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2767                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2768                                          -360.0, 360.0, 1.0, 10.0,
2769                                          labels, values, G_N_ELEMENTS(labels),
2770                                          box3d_angle_y_value_changed );
2771         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2772         g_object_set_data( holder, "box3d_angle_y_action", eact );
2773         box3d_angle_y = eact;
2774     }
2776     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2777         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2778     } else {
2779         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2780     }
2782     /* VP Y state */
2783     {
2784         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2785                                                       // Translators: VP is short for 'vanishing point'
2786                                                       _("State of VP in Y direction"),
2787                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2788                                                       "toggle_vp_y",
2789                                                       Inkscape::ICON_SIZE_DECORATION );
2790         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2791         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2792         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2793         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2794         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2795     }
2797     /* Angle Z */
2798     {
2799         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2800         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2801         eact = create_adjustment_action( "3DBoxAngleZAction",
2802                                          _("Angle in Z direction"), _("Angle Z:"),
2803                                          // Translators: PL is short for 'perspective line'
2804                                          _("Angle of PLs in Z direction"),
2805                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2806                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2807                                          -360.0, 360.0, 1.0, 10.0,
2808                                          labels, values, G_N_ELEMENTS(labels),
2809                                          box3d_angle_z_value_changed );
2810         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2811         g_object_set_data( holder, "box3d_angle_z_action", eact );
2812         box3d_angle_z = eact;
2813     }
2815     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2816         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2817     } else {
2818         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2819     }
2821     /* VP Z state */
2822     {
2823         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2824                                                       // Translators: VP is short for 'vanishing point'
2825                                                       _("State of VP in Z direction"),
2826                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2827                                                       "toggle_vp_z",
2828                                                       Inkscape::ICON_SIZE_DECORATION );
2829         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2830         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2831         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2832         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2833         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2834     }
2836     sigc::connection *connection = new sigc::connection(
2837         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2838        );
2839     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2840     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2843 //########################
2844 //##       Spiral       ##
2845 //########################
2847 static void
2848 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2850     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2852     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2853         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2854     }
2856     // quit if run by the attr_changed listener
2857     if (g_object_get_data( tbl, "freeze" )) {
2858         return;
2859     }
2861     // in turn, prevent listener from responding
2862     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2864     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2866     bool modmade = false;
2867     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2868          items != NULL;
2869          items = items->next)
2870     {
2871         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2872             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2873             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2874             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2875             modmade = true;
2876         }
2877     }
2879     g_free(namespaced_name);
2881     if (modmade) {
2882         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2883                                    _("Change spiral"));
2884     }
2886     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2889 static void
2890 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2892     sp_spl_tb_value_changed(adj, tbl, "revolution");
2895 static void
2896 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2898     sp_spl_tb_value_changed(adj, tbl, "expansion");
2901 static void
2902 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2904     sp_spl_tb_value_changed(adj, tbl, "t0");
2907 static void
2908 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2910     GtkWidget *tbl = GTK_WIDGET(obj);
2912     GtkAdjustment *adj;
2914     // fixme: make settable
2915     gdouble rev = 5;
2916     gdouble exp = 1.0;
2917     gdouble t0 = 0.0;
2919     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2920     gtk_adjustment_set_value(adj, rev);
2921     gtk_adjustment_value_changed(adj);
2923     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2924     gtk_adjustment_set_value(adj, exp);
2925     gtk_adjustment_value_changed(adj);
2927     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2928     gtk_adjustment_set_value(adj, t0);
2929     gtk_adjustment_value_changed(adj);
2931     spinbutton_defocus(GTK_OBJECT(tbl));
2935 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2936                                          gchar const */*old_value*/, gchar const */*new_value*/,
2937                                          bool /*is_interactive*/, gpointer data)
2939     GtkWidget *tbl = GTK_WIDGET(data);
2941     // quit if run by the _changed callbacks
2942     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2943         return;
2944     }
2946     // in turn, prevent callbacks from responding
2947     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2949     GtkAdjustment *adj;
2950     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2951     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2953     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2954     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2956     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2957     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2959     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2963 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2964     NULL, /* child_added */
2965     NULL, /* child_removed */
2966     spiral_tb_event_attr_changed,
2967     NULL, /* content_changed */
2968     NULL  /* order_changed */
2969 };
2971 static void
2972 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2974     int n_selected = 0;
2975     Inkscape::XML::Node *repr = NULL;
2977     purge_repr_listener( tbl, tbl );
2979     for (GSList const *items = selection->itemList();
2980          items != NULL;
2981          items = items->next)
2982     {
2983         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2984             n_selected++;
2985             repr = SP_OBJECT_REPR((SPItem *) items->data);
2986         }
2987     }
2989     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2991     if (n_selected == 0) {
2992         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2993     } else if (n_selected == 1) {
2994         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2996         if (repr) {
2997             g_object_set_data( tbl, "repr", repr );
2998             Inkscape::GC::anchor(repr);
2999             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3000             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3001         }
3002     } else {
3003         // FIXME: implement averaging of all parameters for multiple selected
3004         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3005         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3006     }
3010 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3012     EgeAdjustmentAction* eact = 0;
3013     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3015     {
3016         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3017         ege_output_action_set_use_markup( act, TRUE );
3018         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3019         g_object_set_data( holder, "mode_action", act );
3020     }
3022     /* Revolution */
3023     {
3024         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3025         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3026         eact = create_adjustment_action( "SpiralRevolutionAction",
3027                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3028                                          "tools.shapes.spiral", "revolution", 3.0,
3029                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3030                                          0.01, 1024.0, 0.1, 1.0,
3031                                          labels, values, G_N_ELEMENTS(labels),
3032                                          sp_spl_tb_revolution_value_changed, 1, 2);
3033         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3034     }
3036     /* Expansion */
3037     {
3038         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3039         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3040         eact = create_adjustment_action( "SpiralExpansionAction",
3041                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3042                                          "tools.shapes.spiral", "expansion", 1.0,
3043                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3044                                          0.0, 1000.0, 0.01, 1.0,
3045                                          labels, values, G_N_ELEMENTS(labels),
3046                                          sp_spl_tb_expansion_value_changed);
3047         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3048     }
3050     /* T0 */
3051     {
3052         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3053         gdouble values[] = {0, 0.5, 0.9};
3054         eact = create_adjustment_action( "SpiralT0Action",
3055                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3056                                          "tools.shapes.spiral", "t0", 0.0,
3057                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3058                                          0.0, 0.999, 0.01, 1.0,
3059                                          labels, values, G_N_ELEMENTS(labels),
3060                                          sp_spl_tb_t0_value_changed);
3061         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3062     }
3064     /* Reset */
3065     {
3066         InkAction* inky = ink_action_new( "SpiralResetAction",
3067                                           _("Defaults"),
3068                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3069                                           GTK_STOCK_CLEAR,
3070                                           secondarySize );
3071         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3072         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3073     }
3076     sigc::connection *connection = new sigc::connection(
3077         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3078         );
3079     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3080     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3083 //########################
3084 //##     Pen/Pencil    ##
3085 //########################
3087 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3089     prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3092 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3094     // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3095     //        adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3096     {
3097         EgeOutputAction* act = ege_output_action_new(
3098             tool_is_pencil ?
3099             "FreehandModeActionPencilTemp" :
3100             "FreehandModeActionPenTemp",
3101             _("<b>Mode:</b>"), "", 0 );
3102         ege_output_action_set_use_markup( act, TRUE );
3103         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3104         g_object_set_data( holder, "freehand_mode_action", act );
3105     }
3107     /* Freehand mode toggle buttons */
3108     {
3109         EgeAdjustmentAction* eact = 0;
3110         //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3111         //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3112         guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3113         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3115         {
3116             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3118             GtkTreeIter iter;
3119             gtk_list_store_append( model, &iter );
3120             gtk_list_store_set( model, &iter,
3121                                 0, _("Bézier"),
3122                                 1, _("Regular Bézier mode"),
3123                                 2, "bezier_mode",
3124                                 -1 );
3126             gtk_list_store_append( model, &iter );
3127             gtk_list_store_set( model, &iter,
3128                                 0, _("Spiro"),
3129                                 1, _("Spiro splines mode"),
3130                                 2, "spiro_splines_mode",
3131                                 -1 );
3133             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3134                                                                 "FreehandModeActionPencil" :
3135                                                                 "FreehandModeActionPen",
3136                                                                 (""), (""), NULL, GTK_TREE_MODEL(model) );
3137             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3138             g_object_set_data( holder, "freehande_mode_action", act );
3140             ege_select_one_action_set_appearance( act, "full" );
3141             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3142             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3143             ege_select_one_action_set_icon_column( act, 2 );
3144             ege_select_one_action_set_icon_size( act, secondarySize );
3145             ege_select_one_action_set_tooltip_column( act, 1  );
3147             ege_select_one_action_set_active( act, spiroMode);
3148             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3149         }
3150     }
3153 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3155     sp_add_spiro_toggle(mainActions, holder, false);
3158 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3160     sp_add_spiro_toggle(mainActions, holder, true);
3163 //########################
3164 //##       Tweak        ##
3165 //########################
3167 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3169     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3172 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3174     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3177 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3179     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3182 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3184     int mode = ege_select_one_action_get_active( act );
3185     prefs_set_int_attribute("tools.tweak", "mode", mode);
3187     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3188     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3189     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3190     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3191     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3192     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3193     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3194         if (doh) gtk_action_set_sensitive (doh, TRUE);
3195         if (dos) gtk_action_set_sensitive (dos, TRUE);
3196         if (dol) gtk_action_set_sensitive (dol, TRUE);
3197         if (doo) gtk_action_set_sensitive (doo, TRUE);
3198         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3199         if (fid) gtk_action_set_sensitive (fid, FALSE);
3200     } else {
3201         if (doh) gtk_action_set_sensitive (doh, FALSE);
3202         if (dos) gtk_action_set_sensitive (dos, FALSE);
3203         if (dol) gtk_action_set_sensitive (dol, FALSE);
3204         if (doo) gtk_action_set_sensitive (doo, FALSE);
3205         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3206         if (fid) gtk_action_set_sensitive (fid, TRUE);
3207     }
3210 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3212     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3215 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3216     bool show = gtk_toggle_action_get_active( act );
3217     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3219 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3220     bool show = gtk_toggle_action_get_active( act );
3221     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3223 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3224     bool show = gtk_toggle_action_get_active( act );
3225     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3227 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3228     bool show = gtk_toggle_action_get_active( act );
3229     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3232 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3234     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3236     {
3237         /* Width */
3238         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3239         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3240         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3241                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3242                                                               "tools.tweak", "width", 15,
3243                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3244                                                               1, 100, 1.0, 10.0,
3245                                                               labels, values, G_N_ELEMENTS(labels),
3246                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3247         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3248         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3249     }
3252     {
3253         /* Force */
3254         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3255         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3256         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3257                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3258                                                               "tools.tweak", "force", 20,
3259                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3260                                                               1, 100, 1.0, 10.0,
3261                                                               labels, values, G_N_ELEMENTS(labels),
3262                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3263         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3264         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3265     }
3267     /* Mode */
3268     {
3269         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3271         GtkTreeIter iter;
3272         gtk_list_store_append( model, &iter );
3273         gtk_list_store_set( model, &iter,
3274                             0, _("Push mode"),
3275                             1, _("Push parts of paths in any direction"),
3276                             2, "tweak_push_mode",
3277                             -1 );
3279         gtk_list_store_append( model, &iter );
3280         gtk_list_store_set( model, &iter,
3281                             0, _("Shrink mode"),
3282                             1, _("Shrink (inset) parts of paths"),
3283                             2, "tweak_shrink_mode",
3284                             -1 );
3286         gtk_list_store_append( model, &iter );
3287         gtk_list_store_set( model, &iter,
3288                             0, _("Grow mode"),
3289                             1, _("Grow (outset) parts of paths"),
3290                             2, "tweak_grow_mode",
3291                             -1 );
3293         gtk_list_store_append( model, &iter );
3294         gtk_list_store_set( model, &iter,
3295                             0, _("Attract mode"),
3296                             1, _("Attract parts of paths towards cursor"),
3297                             2, "tweak_attract_mode",
3298                             -1 );
3300         gtk_list_store_append( model, &iter );
3301         gtk_list_store_set( model, &iter,
3302                             0, _("Repel mode"),
3303                             1, _("Repel parts of paths from cursor"),
3304                             2, "tweak_repel_mode",
3305                             -1 );
3307         gtk_list_store_append( model, &iter );
3308         gtk_list_store_set( model, &iter,
3309                             0, _("Roughen mode"),
3310                             1, _("Roughen parts of paths"),
3311                             2, "tweak_roughen_mode",
3312                             -1 );
3314         gtk_list_store_append( model, &iter );
3315         gtk_list_store_set( model, &iter,
3316                             0, _("Color paint mode"),
3317                             1, _("Paint the tool's color upon selected objects"),
3318                             2, "tweak_colorpaint_mode",
3319                             -1 );
3321         gtk_list_store_append( model, &iter );
3322         gtk_list_store_set( model, &iter,
3323                             0, _("Color jitter mode"),
3324                             1, _("Jitter the colors of selected objects"),
3325                             2, "tweak_colorjitter_mode",
3326                             -1 );
3328         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3329         g_object_set( act, "short_label", _("Mode:"), NULL );
3330         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3331         g_object_set_data( holder, "mode_action", act );
3333         ege_select_one_action_set_appearance( act, "full" );
3334         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3335         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3336         ege_select_one_action_set_icon_column( act, 2 );
3337         ege_select_one_action_set_icon_size( act, secondarySize );
3338         ege_select_one_action_set_tooltip_column( act, 1  );
3340         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3341         ege_select_one_action_set_active( act, mode );
3342         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3344         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3345     }
3347     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3349     {
3350         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3351         ege_output_action_set_use_markup( act, TRUE );
3352         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3353         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3354             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3355         g_object_set_data( holder, "tweak_channels_label", act);
3356     }
3358     {
3359         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3360                                                       _("Hue"),
3361                                                       _("In color mode, act on objects' hue"),
3362                                                       NULL,
3363                                                       Inkscape::ICON_SIZE_DECORATION );
3364         //TRANSLATORS:  "H" here stands for hue
3365         g_object_set( act, "short_label", _("H"), NULL );
3366         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3367         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3368         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3369         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3370             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3371         g_object_set_data( holder, "tweak_doh", act);
3372     }
3373     {
3374         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3375                                                       _("Saturation"),
3376                                                       _("In color mode, act on objects' saturation"),
3377                                                       NULL,
3378                                                       Inkscape::ICON_SIZE_DECORATION );
3379         //TRANSLATORS: "S" here stands for Saturation
3380         g_object_set( act, "short_label", _("S"), NULL );
3381         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3382         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3383         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3384         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3385             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3386         g_object_set_data( holder, "tweak_dos", act );
3387     }
3388     {
3389         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3390                                                       _("Lightness"),
3391                                                       _("In color mode, act on objects' lightness"),
3392                                                       NULL,
3393                                                       Inkscape::ICON_SIZE_DECORATION );
3394         //TRANSLATORS: "L" here stands for Lightness
3395         g_object_set( act, "short_label", _("L"), NULL );
3396         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3397         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3398         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3399         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3400             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3401         g_object_set_data( holder, "tweak_dol", act );
3402     }
3403     {
3404         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3405                                                       _("Opacity"),
3406                                                       _("In color mode, act on objects' opacity"),
3407                                                       NULL,
3408                                                       Inkscape::ICON_SIZE_DECORATION );
3409         //TRANSLATORS: "O" here stands for Opacity
3410         g_object_set( act, "short_label", _("O"), NULL );
3411         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3412         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3413         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3414         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3415             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3416         g_object_set_data( holder, "tweak_doo", act );
3417     }
3419     {   /* Fidelity */
3420         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3421         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3422         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3423                                                               _("Fidelity"), _("Fidelity:"),
3424                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3425                                                               "tools.tweak", "fidelity", 50,
3426                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3427                                                               1, 100, 1.0, 10.0,
3428                                                               labels, values, G_N_ELEMENTS(labels),
3429                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3430         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3431         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3432         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3433             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3434         g_object_set_data( holder, "tweak_fidelity", eact );
3435     }
3438     /* Use Pressure button */
3439     {
3440         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3441                                                       _("Pressure"),
3442                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3443                                                       "use_pressure",
3444                                                       Inkscape::ICON_SIZE_DECORATION );
3445         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3446         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3447         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3448     }
3453 //########################
3454 //##     Calligraphy    ##
3455 //########################
3456 static void update_presets_list(GObject *dataKludge ){
3457     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3458     if (sel) {
3459         ege_select_one_action_set_active(sel, 0 );
3460     }
3463 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3465     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3466     update_presets_list(tbl);
3469 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3471     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3472     update_presets_list(tbl);
3475 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3477     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3478     update_presets_list(tbl);
3481 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3483     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3484     update_presets_list(tbl);
3487 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3489     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3490     update_presets_list(tbl);
3493 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3495     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3496     update_presets_list(tbl);
3499 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3501     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3502     update_presets_list(tbl);
3505 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3507     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3508     update_presets_list(tbl);
3511 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3513     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3514     update_presets_list(tbl);
3517 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3519     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3520     update_presets_list(tbl);
3523 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3525     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3526     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3527     update_presets_list(tbl);
3528     if (calligraphy_angle )
3529         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3533 #define PROFILE_FLOAT_SIZE 7
3534 #define PROFILE_INT_SIZE 4
3535 struct ProfileFloatElement {
3536     char const *name;
3537     double def;
3538     double min;
3539     double max;
3540 };
3541 struct ProfileIntElement {
3542     char const *name;
3543     int def;
3544     int min;
3545     int max;
3546 };
3550 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3551     {"mass",0.02, 0.0, 1.0},
3552     {"wiggle",0.0, 0.0, 1.0},
3553     {"angle",30.0, -90.0, 90.0},
3554     {"thinning",0.1, -1.0, 1.0},
3555     {"tremor",0.0, 0.0, 1.0},
3556     {"flatness",0.9, 0.0, 1.0},
3557     {"cap_rounding",0.0, 0.0, 5.0}
3558 };
3559 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3560     {"width",15, 1, 100},
3561     {"usepressure",1,0,1},
3562     {"tracebackground",0,0,1},
3563     {"usetilt",1,0,1},
3564 };
3568 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3569     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3570     if (! desktop) return;
3572     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3573     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3574     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3576     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3577     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3578     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3579     
3580     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3581         ProfileFloatElement const &pe = f_profile[i];
3582         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3583         prefs_set_double_attribute(pref_path,pe.name,v);
3584     }    
3585     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3586         ProfileIntElement const &pe = i_profile[i];
3587         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3588         prefs_set_int_attribute(pref_path,pe.name,v);
3589     }
3590     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3592     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3593     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3594     GtkTreeIter iter;
3595     gtk_list_store_append( model, &iter );
3596     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3598     free(profile_id);
3599     free(pref_path);
3601     ege_select_one_action_set_active(selector, new_index);
3605 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3606     
3607     gint preset_index = ege_select_one_action_get_active( act ); 
3608     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3609        
3610     if ( profile_name) {        
3611         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3612         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3613             ProfileFloatElement const &pe = f_profile[i];
3614             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3615             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3616             if ( adj ) {
3617                 gtk_adjustment_set_value(adj, v);
3618             }
3619         } 
3620         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3621             ProfileIntElement const &pe = i_profile[i];
3622             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3623             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3624             if ( toggle ) {
3625                 gtk_toggle_action_set_active(toggle, v);
3626             } else printf("No toggle");
3627         }
3628         free(profile_name);
3629         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3630     }
3635 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3637     {
3638         EgeAdjustmentAction* calligraphy_angle = 0;
3640         {
3641         /* Width */
3642         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3643         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3644         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3645                                                               _("Pen Width"), _("Width:"),
3646                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3647                                                               "tools.calligraphic", "width", 15,
3648                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3649                                                               1, 100, 1.0, 10.0,
3650                                                               labels, values, G_N_ELEMENTS(labels),
3651                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3652         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3653         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3654         }
3656         {
3657         /* Thinning */
3658             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3659             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3660         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3661                                                               _("Stroke Thinning"), _("Thinning:"),
3662                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3663                                                               "tools.calligraphic", "thinning", 0.1,
3664                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3665                                                               -1.0, 1.0, 0.01, 0.1,
3666                                                               labels, values, G_N_ELEMENTS(labels),
3667                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3668         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3669         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3670         }
3672         {
3673         /* Angle */
3674         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3675         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3676         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3677                                                               _("Pen Angle"), _("Angle:"),
3678                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3679                                                               "tools.calligraphic", "angle", 30,
3680                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3681                                                               -90.0, 90.0, 1.0, 10.0,
3682                                                               labels, values, G_N_ELEMENTS(labels),
3683                                                               sp_ddc_angle_value_changed, 1, 0 );
3684         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3685         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3686         g_object_set_data( holder, "angle", eact );
3687         calligraphy_angle = eact;
3688         }
3690         {
3691         /* Fixation */
3692             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3693         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3694         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3695                                                               _("Fixation"), _("Fixation:"),
3696                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3697                                                               "tools.calligraphic", "flatness", 0.9,
3698                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3699                                                               0.0, 1.0, 0.01, 0.1,
3700                                                               labels, values, G_N_ELEMENTS(labels),
3701                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3702         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3703         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3704         }
3706         {
3707         /* Cap Rounding */
3708             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3709         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3710         // TRANSLATORS: "cap" means "end" (both start and finish) here
3711         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3712                                                               _("Cap rounding"), _("Caps:"),
3713                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3714                                                               "tools.calligraphic", "cap_rounding", 0.0,
3715                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3716                                                               0.0, 5.0, 0.01, 0.1,
3717                                                               labels, values, G_N_ELEMENTS(labels),
3718                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3719         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3720         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3721         }
3723         {
3724         /* Tremor */
3725             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3726         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3727         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3728                                                               _("Stroke Tremor"), _("Tremor:"),
3729                                                               _("Increase to make strokes rugged and trembling"),
3730                                                               "tools.calligraphic", "tremor", 0.0,
3731                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3732                                                               0.0, 1.0, 0.01, 0.1,
3733                                                               labels, values, G_N_ELEMENTS(labels),
3734                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3736         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3737         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3738         }
3740         {
3741         /* Wiggle */
3742         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3743         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3744         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3745                                                               _("Pen Wiggle"), _("Wiggle:"),
3746                                                               _("Increase to make the pen waver and wiggle"),
3747                                                               "tools.calligraphic", "wiggle", 0.0,
3748                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3749                                                               0.0, 1.0, 0.01, 0.1,
3750                                                               labels, values, G_N_ELEMENTS(labels),
3751                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3752         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3753         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3754         }
3756         {
3757         /* Mass */
3758             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3759         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3760         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3761                                                               _("Pen Mass"), _("Mass:"),
3762                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3763                                                               "tools.calligraphic", "mass", 0.02,
3764                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3765                                                               0.0, 1.0, 0.01, 0.1,
3766                                                               labels, values, G_N_ELEMENTS(labels),
3767                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3768         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3769         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3770         }
3773         /* Trace Background button */
3774         {
3775             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3776                                                           _("Trace Background"),
3777                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3778                                                           "trace_background",
3779                                                           Inkscape::ICON_SIZE_DECORATION );
3780             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3781             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3782             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3783             g_object_set_data( holder, "tracebackground", act );
3784         }
3786         /* Use Pressure button */
3787         {
3788             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3789                                                           _("Pressure"),
3790                                                           _("Use the pressure of the input device to alter the width of the pen"),
3791                                                           "use_pressure",
3792                                                           Inkscape::ICON_SIZE_DECORATION );
3793             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3794             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3795             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3796             g_object_set_data( holder, "usepressure", act );
3797         }
3799         /* Use Tilt button */
3800         {
3801             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3802                                                           _("Tilt"),
3803                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3804                                                           "use_tilt",
3805                                                           Inkscape::ICON_SIZE_DECORATION );
3806             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3807             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3808             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3809             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3810             g_object_set_data( holder, "usetilt", act );
3811         }
3813         /*calligraphic profile */
3814         {
3815             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3816             gchar *pref_path;
3819             GtkTreeIter iter;
3820             gtk_list_store_append( model, &iter );
3821             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3823             //TODO: switch back to prefs API
3824             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3825             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3826             int ii=1;
3827             while (child_repr) {
3828                 GtkTreeIter iter;
3829                 char *preset_name = (char *) child_repr->attribute("name");
3830                 gtk_list_store_append( model, &iter );                
3831                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
3832                 child_repr = sp_repr_next(child_repr); 
3833             }
3835             pref_path = NULL;
3836             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
3837             ege_select_one_action_set_appearance( act1, "compact" );
3838             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3839             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3840             g_object_set_data( holder, "profile_selector", act1 );
3841         
3842         }
3844         /*Save or delete calligraphic profile */
3845         {
3846             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3847                                              _("Defaults"),
3848                                              _("Save current settings as new profile"),
3849                                              GTK_STOCK_SAVE );
3850             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3853             gtk_action_group_add_action( mainActions, act );
3854             gtk_action_set_sensitive( act, TRUE );
3855             g_object_set_data( holder, "profile_save_delete", act );
3856         }
3857     }
3861 //########################
3862 //##    Circle / Arc    ##
3863 //########################
3865 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3867     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3868     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3870     if (v1 == 0 && v2 == 0) {
3871         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3872             gtk_action_set_sensitive( ocb, FALSE );
3873             gtk_action_set_sensitive( make_whole, FALSE );
3874         }
3875     } else {
3876         gtk_action_set_sensitive( ocb, TRUE );
3877         gtk_action_set_sensitive( make_whole, TRUE );
3878     }
3881 static void
3882 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3884     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3886     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3887         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3888     }
3890     // quit if run by the attr_changed listener
3891     if (g_object_get_data( tbl, "freeze" )) {
3892         return;
3893     }
3895     // in turn, prevent listener from responding
3896     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3898     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3900     bool modmade = false;
3901     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3902          items != NULL;
3903          items = items->next)
3904     {
3905         SPItem *item = SP_ITEM(items->data);
3907         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3909             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3910             SPArc *arc = SP_ARC(item);
3912             if (!strcmp(value_name, "start"))
3913                 ge->start = (adj->value * M_PI)/ 180;
3914             else
3915                 ge->end = (adj->value * M_PI)/ 180;
3917             sp_genericellipse_normalize(ge);
3918             ((SPObject *)arc)->updateRepr();
3919             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3921             modmade = true;
3922         }
3923     }
3925     g_free(namespaced_name);
3927     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3929     sp_arctb_sensitivize( tbl, adj->value, other->value );
3931     if (modmade) {
3932         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3933                                    _("Arc: Change start/end"));
3934     }
3936     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3940 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3942     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3945 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3947     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3951 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3953     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3954     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
3955     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3956         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
3957     }
3959     // only take action if run by the attr_changed listener
3960     if (!g_object_get_data( tbl, "freeze" )) {
3961         // in turn, prevent listener from responding
3962         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3964         if ( eraserMode != 0 ) {
3965         } else {
3966         }
3967         // TODO finish implementation
3969         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3970     }
3973 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3975     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3976     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3977         if ( ege_select_one_action_get_active( act ) != 0 ) {
3978             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3979         } else {
3980             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3981         }
3982     }
3984     // quit if run by the attr_changed listener
3985     if (g_object_get_data( tbl, "freeze" )) {
3986         return;
3987     }
3989     // in turn, prevent listener from responding
3990     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3992     bool modmade = false;
3994     if ( ege_select_one_action_get_active(act) != 0 ) {
3995         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3996              items != NULL;
3997              items = items->next)
3998         {
3999             if (SP_IS_ARC((SPItem *) items->data)) {
4000                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4001                 repr->setAttribute("sodipodi:open", "true");
4002                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
4003                 modmade = true;
4004             }
4005         }
4006     } else {
4007         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4008              items != NULL;
4009              items = items->next)
4010         {
4011             if (SP_IS_ARC((SPItem *) items->data))    {
4012                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4013                 repr->setAttribute("sodipodi:open", NULL);
4014                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
4015                 modmade = true;
4016             }
4017         }
4018     }
4020     if (modmade) {
4021         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4022                                    _("Arc: Change open/closed"));
4023     }
4025     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4028 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4030     GtkAdjustment *adj;
4031     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4032     gtk_adjustment_set_value(adj, 0.0);
4033     gtk_adjustment_value_changed(adj);
4035     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4036     gtk_adjustment_set_value(adj, 0.0);
4037     gtk_adjustment_value_changed(adj);
4039     spinbutton_defocus( GTK_OBJECT(obj) );
4042 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4043                                       gchar const */*old_value*/, gchar const */*new_value*/,
4044                                       bool /*is_interactive*/, gpointer data)
4046     GObject *tbl = G_OBJECT(data);
4048     // quit if run by the _changed callbacks
4049     if (g_object_get_data( tbl, "freeze" )) {
4050         return;
4051     }
4053     // in turn, prevent callbacks from responding
4054     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4056     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4057     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4059     GtkAdjustment *adj1,*adj2;
4060     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4061     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4062     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4063     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4065     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4067     char const *openstr = NULL;
4068     openstr = repr->attribute("sodipodi:open");
4069     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4071     if (openstr) {
4072         ege_select_one_action_set_active( ocb, 1 );
4073     } else {
4074         ege_select_one_action_set_active( ocb, 0 );
4075     }
4077     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4080 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4081     NULL, /* child_added */
4082     NULL, /* child_removed */
4083     arc_tb_event_attr_changed,
4084     NULL, /* content_changed */
4085     NULL  /* order_changed */
4086 };
4089 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4091     int n_selected = 0;
4092     Inkscape::XML::Node *repr = NULL;
4094     purge_repr_listener( tbl, tbl );
4096     for (GSList const *items = selection->itemList();
4097          items != NULL;
4098          items = items->next)
4099     {
4100         if (SP_IS_ARC((SPItem *) items->data)) {
4101             n_selected++;
4102             repr = SP_OBJECT_REPR((SPItem *) items->data);
4103         }
4104     }
4106     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4108     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4109     if (n_selected == 0) {
4110         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4111     } else if (n_selected == 1) {
4112         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4113         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4115         if (repr) {
4116             g_object_set_data( tbl, "repr", repr );
4117             Inkscape::GC::anchor(repr);
4118             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4119             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4120         }
4121     } else {
4122         // FIXME: implement averaging of all parameters for multiple selected
4123         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4124         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4125         sp_arctb_sensitivize( tbl, 1, 0 );
4126     }
4130 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4132     EgeAdjustmentAction* eact = 0;
4133     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4136     {
4137         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4138         ege_output_action_set_use_markup( act, TRUE );
4139         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4140         g_object_set_data( holder, "mode_action", act );
4141     }
4143     /* Start */
4144     {
4145         eact = create_adjustment_action( "ArcStartAction",
4146                                          _("Start"), _("Start:"),
4147                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4148                                          "tools.shapes.arc", "start", 0.0,
4149                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4150                                          -360.0, 360.0, 1.0, 10.0,
4151                                          0, 0, 0,
4152                                          sp_arctb_start_value_changed);
4153         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4154     }
4156     /* End */
4157     {
4158         eact = create_adjustment_action( "ArcEndAction",
4159                                          _("End"), _("End:"),
4160                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4161                                          "tools.shapes.arc", "end", 0.0,
4162                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4163                                          -360.0, 360.0, 1.0, 10.0,
4164                                          0, 0, 0,
4165                                          sp_arctb_end_value_changed);
4166         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4167     }
4169     /* Segments / Pie checkbox */
4170     {
4171         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4173         GtkTreeIter iter;
4174         gtk_list_store_append( model, &iter );
4175         gtk_list_store_set( model, &iter,
4176                             0, _("Closed arc"),
4177                             1, _("Switch to segment (closed shape with two radii)"),
4178                             2, "circle_closed_arc",
4179                             -1 );
4181         gtk_list_store_append( model, &iter );
4182         gtk_list_store_set( model, &iter,
4183                             0, _("Open Arc"),
4184                             1, _("Switch to arc (unclosed shape)"),
4185                             2, "circle_open_arc",
4186                             -1 );
4188         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4189         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4190         g_object_set_data( holder, "open_action", act );
4192         ege_select_one_action_set_appearance( act, "full" );
4193         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4194         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4195         ege_select_one_action_set_icon_column( act, 2 );
4196         ege_select_one_action_set_icon_size( act, secondarySize );
4197         ege_select_one_action_set_tooltip_column( act, 1  );
4199         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4200         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4201         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4202         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4203     }
4205     /* Make Whole */
4206     {
4207         InkAction* inky = ink_action_new( "ArcResetAction",
4208                                           _("Make whole"),
4209                                           _("Make the shape a whole ellipse, not arc or segment"),
4210                                           "reset_circle",
4211                                           secondarySize );
4212         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4213         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4214         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4215         g_object_set_data( holder, "make_whole", inky );
4216     }
4218     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4219     // sensitivize make whole and open checkbox
4220     {
4221         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4222         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4223         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4224     }
4227     sigc::connection *connection = new sigc::connection(
4228         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4229         );
4230     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4231     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4237 // toggle button callbacks and updaters
4239 //########################
4240 //##      Dropper       ##
4241 //########################
4243 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4244     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4245     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4246     if ( set_action ) {
4247         if ( gtk_toggle_action_get_active( act ) ) {
4248             gtk_action_set_sensitive( set_action, TRUE );
4249         } else {
4250             gtk_action_set_sensitive( set_action, FALSE );
4251         }
4252     }
4254     spinbutton_defocus(GTK_OBJECT(tbl));
4257 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4258     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4259     spinbutton_defocus(GTK_OBJECT(tbl));
4263 /**
4264  * Dropper auxiliary toolbar construction and setup.
4265  *
4266  * TODO: Would like to add swatch of current color.
4267  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4268  *       can drag and drop places. Will provide a nice mixing palette.
4269  */
4270 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4272     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4274     {
4275         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4276         ege_output_action_set_use_markup( act, TRUE );
4277         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4278     }
4280     {
4281         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4282                                                       _("Pick opacity"),
4283                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4284                                                       NULL,
4285                                                       Inkscape::ICON_SIZE_DECORATION );
4286         g_object_set( act, "short_label", _("Pick"), NULL );
4287         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4288         g_object_set_data( holder, "pick_action", act );
4289         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4290         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4291     }
4293     {
4294         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4295                                                       _("Assign opacity"),
4296                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4297                                                       NULL,
4298                                                       Inkscape::ICON_SIZE_DECORATION );
4299         g_object_set( act, "short_label", _("Assign"), NULL );
4300         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4301         g_object_set_data( holder, "set_action", act );
4302         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4303         // make sure it's disabled if we're not picking alpha
4304         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4305         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4306     }
4311 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4313     {
4314         /* Width */
4315         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4316         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4317         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4318                                                               _("Pen Width"), _("Width:"),
4319                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4320                                                               "tools.eraser", "width", 15,
4321                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4322                                                               1, 100, 1.0, 10.0,
4323                                                               labels, values, G_N_ELEMENTS(labels),
4324                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4325         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4326         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4327     }
4329     {
4330         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4332         GtkTreeIter iter;
4333         gtk_list_store_append( model, &iter );
4334         gtk_list_store_set( model, &iter,
4335                             0, _("Delete"),
4336                             1, _("Delete objects touched by the eraser"),
4337                             2, "delete_object",
4338                             -1 );
4340         gtk_list_store_append( model, &iter );
4341         gtk_list_store_set( model, &iter,
4342                             0, _("Cut"),
4343                             1, _("Cut out from objects"),
4344                             2, "difference",
4345                             -1 );
4347         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4348         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4349         g_object_set_data( holder, "eraser_mode_action", act );
4351         ege_select_one_action_set_appearance( act, "full" );
4352         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4353         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4354         ege_select_one_action_set_icon_column( act, 2 );
4355         ege_select_one_action_set_tooltip_column( act, 1  );
4357         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4358         ege_select_one_action_set_active( act, eraserMode );
4359         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4360     }
4361     
4364 //########################
4365 //##    Text Toolbox    ##
4366 //########################
4367 /*
4368 static void
4369 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4371     //Call back for letter sizing spinbutton
4374 static void
4375 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4377     //Call back for line height spinbutton
4380 static void
4381 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4383     //Call back for horizontal kerning spinbutton
4386 static void
4387 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4389     //Call back for vertical kerning spinbutton
4392 static void
4393 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4395     //Call back for letter rotation spinbutton
4396 }*/
4398 namespace {
4400 bool popdown_visible = false;
4401 bool popdown_hasfocus = false;
4403 void
4404 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4406     SPStyle *query =
4407         sp_style_new (SP_ACTIVE_DOCUMENT);
4409     int result_fontspec =
4410         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4412     int result_family =
4413         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4415     int result_style =
4416         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4418     int result_numbers =
4419         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4421     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4423     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4424     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4425     {
4426         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4428         if (repr)
4429         {
4430             sp_style_read_from_repr (query, repr);
4431         }
4432         else
4433         {
4434             return;
4435         }
4436     }
4438     if (query->text)
4439     {
4440         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4441             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4442             gtk_entry_set_text (GTK_ENTRY (entry), "");
4444         } else if (query->text->font_specification.value || query->text->font_family.value) {
4446             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4448             // Get the font that corresponds
4449             Glib::ustring familyName;
4451             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4452             if (font) {
4453                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4454                 font->Unref();
4455                 font = NULL;
4456             }
4458             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4460             Gtk::TreePath path;
4461             try {
4462                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4463             } catch (...) {
4464                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4465                 return;
4466             }
4468             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4469             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4471             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4473             gtk_tree_selection_select_path (tselection, path.gobj());
4474             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4476             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4477         }
4479         //Size
4480         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4481         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4482         g_object_set_data (tbl, "size-block", gpointer(1));
4483         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4484         g_object_set_data (tbl, "size-block", gpointer(0));
4485         free (str);
4487         //Anchor
4488         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4489         {
4490             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4491             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4492             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4493             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4494         }
4495         else
4496         {
4497             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4498             {
4499                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4500                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4501                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4502                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4503             }
4504             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4505             {
4506                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4507                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4508                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4509                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4510             }
4511             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4512             {
4513                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4514                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4515                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4516                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4517             }
4518         }
4520         //Style
4521         {
4522             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4524             gboolean active = gtk_toggle_button_get_active (button);
4525             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4527             if (active != check)
4528             {
4529                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4530                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4531                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4532             }
4533         }
4535         {
4536             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4538             gboolean active = gtk_toggle_button_get_active (button);
4539             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4541             if (active != check)
4542             {
4543                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4544                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4545                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4546             }
4547         }
4549         //Orientation
4550         //locking both buttons, changing one affect all group (both)
4551         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4552         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4554         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4555         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4557         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4558         {
4559             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4560         }
4561         else
4562         {
4563             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4564         }
4565         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4566         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4567     }
4569     sp_style_unref(query);
4572 void
4573 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4575     sp_text_toolbox_selection_changed (selection, tbl);
4578 void
4579 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4581     sp_text_toolbox_selection_changed (NULL, tbl);
4584 void
4585 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4586                                 GObject             *tbl)
4588     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4589     GtkTreeModel *model = 0;
4590     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4591     GtkTreeIter   iter;
4592     char         *family = 0;
4594     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4595     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4597     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4598         return;
4599     }
4601     gtk_tree_model_get (model, &iter, 0, &family, -1);
4603     if (g_object_get_data (G_OBJECT (selection), "block"))
4604     {
4605         gtk_entry_set_text (GTK_ENTRY (entry), family);
4606         return;
4607     }
4609     gtk_entry_set_text (GTK_ENTRY (entry), family);
4611     SPStyle *query =
4612         sp_style_new (SP_ACTIVE_DOCUMENT);
4614     int result_fontspec =
4615         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4617     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4619     SPCSSAttr *css = sp_repr_css_attr_new ();
4622     // First try to get the font spec from the stored value
4623     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4625     if (fontSpec.empty()) {
4626         // Construct a new font specification if it does not yet exist
4627         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4628         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4629         fontFromStyle->Unref();
4630     }
4632     if (!fontSpec.empty()) {
4633         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4634         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4635             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4636             if (font) {
4637                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4639                 // Set all the these just in case they were altered when finding the best
4640                 // match for the new family and old style...
4642                 gchar c[256];
4644                 font->Family(c, 256);
4645                 sp_repr_css_set_property (css, "font-family", c);
4647                 font->Attribute( "weight", c, 256);
4648                 sp_repr_css_set_property (css, "font-weight", c);
4650                 font->Attribute("style", c, 256);
4651                 sp_repr_css_set_property (css, "font-style", c);
4653                 font->Attribute("stretch", c, 256);
4654                 sp_repr_css_set_property (css, "font-stretch", c);
4656                 font->Attribute("variant", c, 256);
4657                 sp_repr_css_set_property (css, "font-variant", c);
4659                 font->Unref();
4660             }
4661         }
4662     }
4664     // If querying returned nothing, set the default style of the tool (for new texts)
4665     if (result_fontspec == QUERY_STYLE_NOTHING)
4666     {
4667         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4668         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4669     }
4670     else
4671     {
4672         sp_desktop_set_style (desktop, css, true, true);
4673     }
4675     sp_style_unref(query);
4677     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4678                                    _("Text: Change font family"));
4679     sp_repr_css_attr_unref (css);
4680     free (family);
4681     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4683     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4686 void
4687 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4688                                        GObject      *tbl)
4690     const char *family = gtk_entry_get_text (entry);
4692     try {
4693         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4694         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4695         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4696         gtk_tree_selection_select_path (selection, path.gobj());
4697         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4698         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4699     } catch (...) {
4700         if (family && strlen (family))
4701         {
4702             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4703         }
4704     }
4707 void
4708 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4709                                    gpointer          data)
4711     if (g_object_get_data (G_OBJECT (button), "block")) return;
4712     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4713     int prop = GPOINTER_TO_INT(data);
4715     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4716     SPCSSAttr *css = sp_repr_css_attr_new ();
4718     switch (prop)
4719     {
4720         case 0:
4721         {
4722             sp_repr_css_set_property (css, "text-anchor", "start");
4723             sp_repr_css_set_property (css, "text-align", "start");
4724             break;
4725         }
4726         case 1:
4727         {
4728             sp_repr_css_set_property (css, "text-anchor", "middle");
4729             sp_repr_css_set_property (css, "text-align", "center");
4730             break;
4731         }
4733         case 2:
4734         {
4735             sp_repr_css_set_property (css, "text-anchor", "end");
4736             sp_repr_css_set_property (css, "text-align", "end");
4737             break;
4738         }
4740         case 3:
4741         {
4742             sp_repr_css_set_property (css, "text-anchor", "start");
4743             sp_repr_css_set_property (css, "text-align", "justify");
4744             break;
4745         }
4746     }
4748     SPStyle *query =
4749         sp_style_new (SP_ACTIVE_DOCUMENT);
4750     int result_numbers =
4751         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4753     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4754     if (result_numbers == QUERY_STYLE_NOTHING)
4755     {
4756         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4757     }
4759     sp_style_unref(query);
4761     sp_desktop_set_style (desktop, css, true, true);
4762     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4763                                    _("Text: Change alignment"));
4764     sp_repr_css_attr_unref (css);
4766     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4769 void
4770 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4771                                gpointer          data)
4773     if (g_object_get_data (G_OBJECT (button), "block")) return;
4775     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4776     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4777     int          prop       = GPOINTER_TO_INT(data);
4778     bool         active     = gtk_toggle_button_get_active (button);
4780     SPStyle *query =
4781         sp_style_new (SP_ACTIVE_DOCUMENT);
4783     int result_fontspec =
4784         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4786     int result_family =
4787         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4789     int result_style =
4790         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4792     int result_numbers =
4793         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4795     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4796     Glib::ustring newFontSpec = "";
4798     if (fontSpec.empty()) {
4799         // Construct a new font specification if it does not yet exist
4800         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4801         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4802         fontFromStyle->Unref();
4803     }
4805     switch (prop)
4806     {
4807         case 0:
4808         {
4809             if (!fontSpec.empty()) {
4810                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4811             }
4812             if (fontSpec != newFontSpec) {
4813                 // Don't even set the bold if the font didn't exist on the system
4814                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4815             }
4816             break;
4817         }
4819         case 1:
4820         {
4821             if (!fontSpec.empty()) {
4822                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4823             }
4824             if (fontSpec != newFontSpec) {
4825                 // Don't even set the italic if the font didn't exist on the system
4826                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4827             }
4828             break;
4829         }
4830     }
4832     if (!newFontSpec.empty()) {
4833         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4834     }
4836     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4837     if (result_fontspec == QUERY_STYLE_NOTHING)
4838     {
4839         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4840     }
4842     sp_style_unref(query);
4844     sp_desktop_set_style (desktop, css, true, true);
4845     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4846                                    _("Text: Change font style"));
4847     sp_repr_css_attr_unref (css);
4849     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4852 void
4853 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4854                                      gpointer         data)
4856     if (g_object_get_data (G_OBJECT (button), "block")) {
4857         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4858         return;
4859     }
4861     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4862     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4863     int          prop       = GPOINTER_TO_INT(data);
4865     switch (prop)
4866     {
4867         case 0:
4868         {
4869             sp_repr_css_set_property (css, "writing-mode", "lr");
4870             break;
4871         }
4873         case 1:
4874         {
4875             sp_repr_css_set_property (css, "writing-mode", "tb");
4876             break;
4877         }
4878     }
4880     SPStyle *query =
4881         sp_style_new (SP_ACTIVE_DOCUMENT);
4882     int result_numbers =
4883         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4885     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4886     if (result_numbers == QUERY_STYLE_NOTHING)
4887     {
4888         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4889     }
4891     sp_desktop_set_style (desktop, css, true, true);
4892     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4893                                    _("Text: Change orientation"));
4894     sp_repr_css_attr_unref (css);
4896     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4899 gboolean
4900 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4902     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4903     if (!desktop) return FALSE;
4905     switch (get_group0_keyval (event)) {
4906         case GDK_Escape: // defocus
4907             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4908             sp_text_toolbox_selection_changed (NULL, tbl); // update
4909             return TRUE; // I consumed the event
4910             break;
4911     }
4912     return FALSE;
4915 gboolean
4916 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4918     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4919     if (!desktop) return FALSE;
4921     switch (get_group0_keyval (event)) {
4922         case GDK_KP_Enter:
4923         case GDK_Return:
4924         case GDK_Escape: // defocus
4925             gtk_widget_hide (w);
4926             popdown_visible = false;
4927             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4928             return TRUE; // I consumed the event
4929             break;
4930         case GDK_w:
4931         case GDK_W:
4932             if (event->state & GDK_CONTROL_MASK) {
4933                 gtk_widget_hide (w);
4934                 popdown_visible = false;
4935                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4936                 return TRUE; // I consumed the event
4937             }
4938             break;
4939     }
4940     return FALSE;
4944 void
4945 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4946                                GObject     *tbl)
4948     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4950     if (g_object_get_data (tbl, "size-block")) return;
4952     // If this is not from selecting a size in the list (in which case get_active will give the
4953     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4954     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4955     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4956     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4957         return;
4959     gchar *endptr;
4960     gdouble value = -1;
4961     char *text = gtk_combo_box_get_active_text (cbox);
4962     if (text) {
4963         value = g_strtod (text, &endptr);
4964         if (endptr == text) // conversion failed, non-numeric input
4965             value = -1;
4966         free (text);
4967     }
4968     if (value <= 0) {
4969         return; // could not parse value
4970     }
4972     SPCSSAttr *css = sp_repr_css_attr_new ();
4973     Inkscape::CSSOStringStream osfs;
4974     osfs << value;
4975     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4977     SPStyle *query =
4978         sp_style_new (SP_ACTIVE_DOCUMENT);
4979     int result_numbers =
4980         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4982     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4983     if (result_numbers == QUERY_STYLE_NOTHING)
4984     {
4985         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4986     }
4988     sp_style_unref(query);
4990     sp_desktop_set_style (desktop, css, true, true);
4991     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4992                                    _("Text: Change font size"));
4993     sp_repr_css_attr_unref (css);
4995     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4998 gboolean
4999 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5001     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5002     if (!desktop) return FALSE;
5004     if (!g_object_get_data (tbl, "esc-pressed")) {
5005         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5006         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5007         sp_text_toolbox_size_changed (cbox, tbl);
5008         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5009     }
5010     return FALSE; // I consumed the event
5014 gboolean
5015 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5017     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5018     if (!desktop) return FALSE;
5020     switch (get_group0_keyval (event)) {
5021         case GDK_Escape: // defocus
5022             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5023             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5024             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5025             return TRUE; // I consumed the event
5026             break;
5027         case GDK_Return: // defocus
5028         case GDK_KP_Enter:
5029             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5030             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5031             sp_text_toolbox_size_changed (cbox, tbl);
5032             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5033             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5034             return TRUE; // I consumed the event
5035             break;
5036     }
5037     return FALSE;
5040 void
5041 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5042                                          GObject            *tbl)
5044     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5045     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5046     int x, y;
5048     if (!popdown_visible)
5049     {
5050         gdk_window_get_origin (widget->window, &x, &y);
5051         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5052         gtk_widget_show_all (popdown);
5053         //sp_transientize (popdown);
5055         gdk_pointer_grab (widget->window, TRUE,
5056                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5057                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5058                                         GDK_POINTER_MOTION_MASK),
5059                           NULL, NULL, GDK_CURRENT_TIME);
5061         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5063         popdown_visible = true;
5064     }
5065     else
5066     {
5067         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5068         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5069         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5070         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5071         gtk_widget_hide (popdown);
5072         popdown_visible = false;
5073     }
5076 gboolean
5077 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5078                                  GdkEventFocus    */*event*/,
5079                                  GObject          */*tbl*/)
5081     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5082     return FALSE;
5085 gboolean
5086 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5087                                    GdkEventFocus    */*event*/,
5088                                    GObject          */*tbl*/)
5090     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5092     if (popdown_hasfocus) {
5093         gtk_widget_hide (popdown);
5094         popdown_hasfocus = false;
5095         popdown_visible = false;
5096         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5097         return TRUE;
5098     }
5099     return FALSE;
5102 gboolean
5103 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5104                                    GdkEventFocus    */*event*/,
5105                                    GObject          */*tbl*/)
5107     popdown_hasfocus = true;
5108     return TRUE;
5112 void
5113 cell_data_func  (GtkTreeViewColumn */*column*/,
5114                  GtkCellRenderer   *cell,
5115                  GtkTreeModel      *tree_model,
5116                  GtkTreeIter       *iter,
5117                  gpointer           /*data*/)
5119     char        *family,
5120         *family_escaped,
5121         *sample_escaped;
5123     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5125     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5127     family_escaped = g_markup_escape_text (family, -1);
5128     sample_escaped = g_markup_escape_text (sample, -1);
5130     std::stringstream markup;
5131     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5132     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5134     free (family);
5135     free (family_escaped);
5136     free (sample_escaped);
5139 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5140     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5141     if (completion) {
5142         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5143         g_object_unref (completion);
5144     }
5147 GtkWidget*
5148 sp_text_toolbox_new (SPDesktop *desktop)
5150     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
5151     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5153     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5154     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5156     GtkTooltips *tt = gtk_tooltips_new();
5157     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5159     ////////////Family
5160     //Window
5161     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5162     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5164     //Entry
5165     GtkWidget           *entry = gtk_entry_new ();
5166     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5167     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5168     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5169     gtk_entry_completion_set_text_column (completion, 0);
5170     gtk_entry_completion_set_minimum_key_length (completion, 1);
5171     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5172     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5173     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5174     aux_toolbox_space (tbl, 1);
5175     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5176     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5178     //Button
5179     GtkWidget   *button = gtk_button_new ();
5180     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5181     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5183     //Popdown
5184     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5185     GtkWidget           *treeview = gtk_tree_view_new ();
5187     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5188     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5189     gtk_tree_view_column_pack_start (column, cell, FALSE);
5190     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5191     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5192     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5194     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5195     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5196     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5198     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5200     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5201     gtk_container_add (GTK_CONTAINER (sw), treeview);
5203     gtk_container_add (GTK_CONTAINER (window), sw);
5204     gtk_widget_set_size_request (window, 300, 450);
5206     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5207     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5208     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5210     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5212     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5213     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5214     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5216     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5217     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5219     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5220     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5221     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5222     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5223     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5225     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5226     aux_toolbox_space (tbl, 1);
5227     GtkWidget *box = gtk_event_box_new ();
5228     gtk_container_add (GTK_CONTAINER (box), image);
5229     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5230     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5231     GtkTooltips *tooltips = gtk_tooltips_new ();
5232     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5233     gtk_widget_hide (GTK_WIDGET (box));
5234     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5236     ////////////Size
5237     const char *sizes[] = {
5238         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5239         "16", "18", "20", "22", "24", "28",
5240         "32", "36", "40", "48", "56", "64", "72", "144"
5241     };
5243     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5244     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5245     gtk_widget_set_size_request (cbox, 80, -1);
5246     aux_toolbox_space (tbl, 1);
5247     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5248     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5249     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5250     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5251     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5253     //spacer
5254     aux_toolbox_space (tbl, 4);
5255     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5257     ////////////Text anchor
5258     GtkWidget *group   = gtk_radio_button_new (NULL);
5259     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5260     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5262     // left
5263     GtkWidget *rbutton = group;
5264     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5265     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5266     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5268     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5269     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5270     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5271     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5273     // center
5274     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5275     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5276     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5277     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5279     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5280     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5281     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5282     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5284     // right
5285     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5286     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5287     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5288     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5290     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5291     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5292     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5293     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5295     // fill
5296     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5297     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5298     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5299     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5301     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5302     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5303     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5304     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5306     aux_toolbox_space (tbl, 1);
5307     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5309     //spacer
5310     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5312     ////////////Text style
5313     row = gtk_hbox_new (FALSE, 4);
5315     // bold
5316     rbutton = gtk_toggle_button_new ();
5317     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5318     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5319     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5320     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5322     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5323     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5324     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5326     // italic
5327     rbutton = gtk_toggle_button_new ();
5328     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5329     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5330     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5331     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5333     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5334     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5335     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5337     aux_toolbox_space (tbl, 1);
5338     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5340     //spacer
5341     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5343     ////////////Text orientation
5344     group   = gtk_radio_button_new (NULL);
5345     row     = gtk_hbox_new (FALSE, 4);
5346     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5348     // horizontal
5349     rbutton = group;
5350     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5351     gtk_container_add           (GTK_CONTAINER (rbutton),
5352                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5353     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5354     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5356     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5357     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5358     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5360     // vertical
5361     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5362     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5363     gtk_container_add           (GTK_CONTAINER (rbutton),
5364                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5365     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5366     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5368     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5369     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5370     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5371     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5374     //watch selection
5375     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5377     sigc::connection *c_selection_changed =
5378         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5379                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5380     pool->add_connection ("selection-changed", c_selection_changed);
5382     sigc::connection *c_selection_modified =
5383         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5384                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5385     pool->add_connection ("selection-modified", c_selection_modified);
5387     sigc::connection *c_subselection_changed =
5388         new sigc::connection (desktop->connectToolSubselectionChanged
5389                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5390     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5392     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5395     gtk_widget_show_all (tbl);
5396     return tbl;
5398 } // end of sp_text_toolbox_new()
5400 }//<unnamed> namespace
5403 //#########################
5404 //##      Connector      ##
5405 //#########################
5407 static void sp_connector_path_set_avoid(void)
5409     cc_selection_set_avoid(true);
5413 static void sp_connector_path_set_ignore(void)
5415     cc_selection_set_avoid(false);
5420 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5422     // quit if run by the _changed callbacks
5423     if (g_object_get_data( tbl, "freeze" )) {
5424         return;
5425     }
5427     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5428     SPDocument *doc = sp_desktop_document(desktop);
5430     if (!sp_document_get_undo_sensitive(doc))
5431     {
5432         return;
5433     }
5435     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5437     if ( repr->attribute("inkscape:connector-spacing") ) {
5438         gdouble priorValue = gtk_adjustment_get_value(adj);
5439         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5440         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5441             return;
5442         }
5443     } else if ( adj->value == defaultConnSpacing ) {
5444         return;
5445     }
5447     // in turn, prevent callbacks from responding
5448     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5450     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5451     SP_OBJECT(desktop->namedview)->updateRepr();
5453     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5454     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5455         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5456         NR::Matrix m = NR::identity();
5457         avoid_item_move(&m, item);
5458     }
5460     if (items) {
5461         g_slist_free(items);
5462     }
5464     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5465             _("Change connector spacing"));
5467     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5469     spinbutton_defocus(GTK_OBJECT(tbl));
5472 static void sp_connector_graph_layout(void)
5474     if (!SP_ACTIVE_DESKTOP) return;
5476     // hack for clones, see comment in align-and-distribute.cpp
5477     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5478     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5480     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5482     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5484     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5487 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5489     if ( gtk_toggle_action_get_active( act ) ) {
5490         prefs_set_string_attribute("tools.connector", "directedlayout",
5491                 "true");
5492     } else {
5493         prefs_set_string_attribute("tools.connector", "directedlayout",
5494                 "false");
5495     }
5498 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5500     if ( gtk_toggle_action_get_active( act ) ) {
5501         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5502                 "true");
5503     } else {
5504         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5505                 "false");
5506     }
5510 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5512     prefs_set_double_attribute("tools.connector", "length", adj->value);
5513     spinbutton_defocus(GTK_OBJECT(tbl));
5516 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5517                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5518                                             bool /*is_interactive*/, gpointer data)
5520     GtkWidget *tbl = GTK_WIDGET(data);
5522     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5523         return;
5524     }
5525     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5526         return;
5527     }
5529     GtkAdjustment *adj = (GtkAdjustment*)
5530             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5531     gdouble spacing = defaultConnSpacing;
5532     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5534     gtk_adjustment_set_value(adj, spacing);
5538 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5539     NULL, /* child_added */
5540     NULL, /* child_removed */
5541     connector_tb_event_attr_changed,
5542     NULL, /* content_changed */
5543     NULL  /* order_changed */
5544 };
5547 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5549     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5551     {
5552         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5553                                           _("Avoid"),
5554                                           _("Make connectors avoid selected objects"),
5555                                           "connector_avoid",
5556                                           secondarySize );
5557         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5558         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5559     }
5561     {
5562         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5563                                           _("Ignore"),
5564                                           _("Make connectors ignore selected objects"),
5565                                           "connector_ignore",
5566                                           secondarySize );
5567         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5568         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5569     }
5571     EgeAdjustmentAction* eact = 0;
5573     // Spacing spinbox
5574     eact = create_adjustment_action( "ConnectorSpacingAction",
5575                                      _("Connector Spacing"), _("Spacing:"),
5576                                      _("The amount of space left around objects by auto-routing connectors"),
5577                                      "tools.connector", "spacing", defaultConnSpacing,
5578                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5579                                      0, 100, 1.0, 10.0,
5580                                      0, 0, 0,
5581                                      connector_spacing_changed, 1, 0 );
5582     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5584     // Graph (connector network) layout
5585     {
5586         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5587                                           _("Graph"),
5588                                           _("Nicely arrange selected connector network"),
5589                                           "graph_layout",
5590                                           secondarySize );
5591         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5592         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5593     }
5595     // Default connector length spinbox
5596     eact = create_adjustment_action( "ConnectorLengthAction",
5597                                      _("Connector Length"), _("Length:"),
5598                                      _("Ideal length for connectors when layout is applied"),
5599                                      "tools.connector", "length", 100,
5600                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5601                                      10, 1000, 10.0, 100.0,
5602                                      0, 0, 0,
5603                                      connector_length_changed, 1, 0 );
5604     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5607     // Directed edges toggle button
5608     {
5609         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5610                                                       _("Downwards"),
5611                                                       _("Make connectors with end-markers (arrows) point downwards"),
5612                                                       "directed_graph",
5613                                                       Inkscape::ICON_SIZE_DECORATION );
5614         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5616         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5617         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5618                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5620         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5621     }
5623     // Avoid overlaps toggle button
5624     {
5625         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5626                                                       _("Remove overlaps"),
5627                                                       _("Do not allow overlapping shapes"),
5628                                                       "remove_overlaps",
5629                                                       Inkscape::ICON_SIZE_DECORATION );
5630         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5632         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5633         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5634                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5636         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5637     }
5639     // Code to watch for changes to the connector-spacing attribute in
5640     // the XML.
5641     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5642     g_assert(repr != NULL);
5644     purge_repr_listener( holder, holder );
5646     if (repr) {
5647         g_object_set_data( holder, "repr", repr );
5648         Inkscape::GC::anchor(repr);
5649         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5650         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5651     }
5652 } // end of sp_connector_toolbox_prep()
5655 //#########################
5656 //##     Paintbucket     ##
5657 //#########################
5659 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5661     gint channels = ege_select_one_action_get_active( act );
5662     flood_channels_set_channels( channels );
5665 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5667     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5670 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5672     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5675 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5677     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5678     SPUnit const *unit = tracker->getActiveUnit();
5680     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5682     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5685 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5687     // FIXME: make defaults settable via Inkscape Options
5688     struct KeyValue {
5689         char const *key;
5690         double value;
5691     } const key_values[] = {
5692         {"threshold", 15},
5693         {"offset", 0.0}
5694     };
5696     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5697         KeyValue const &kv = key_values[i];
5698         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5699         if ( adj ) {
5700             gtk_adjustment_set_value(adj, kv.value);
5701         }
5702     }
5704     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5705     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5706     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5707     ege_select_one_action_set_active( autogap_action, 0 );
5710 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5712     EgeAdjustmentAction* eact = 0;
5714     {
5715         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5717         GList* items = 0;
5718         gint count = 0;
5719         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5720         {
5721             GtkTreeIter iter;
5722             gtk_list_store_append( model, &iter );
5723             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5724             count++;
5725         }
5726         g_list_free( items );
5727         items = 0;
5728         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5729         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5730         ege_select_one_action_set_appearance( act1, "compact" );
5731         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5732         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5733         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5734         g_object_set_data( holder, "channels_action", act1 );
5735     }
5737     // Spacing spinbox
5738     {
5739         eact = create_adjustment_action(
5740             "ThresholdAction",
5741             _("Fill Threshold"), _("Threshold:"),
5742             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5743             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5744             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5745             0, 0, 0,
5746             paintbucket_threshold_changed, 1, 0 );
5748         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5749     }
5751     // Create the units menu.
5752     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5753     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5754     if (stored_unit)
5755         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5756     g_object_set_data( holder, "tracker", tracker );
5757     {
5758         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5759         gtk_action_group_add_action( mainActions, act );
5760     }
5762     // Offset spinbox
5763     {
5764         eact = create_adjustment_action(
5765             "OffsetAction",
5766             _("Grow/shrink by"), _("Grow/shrink by:"),
5767             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5768             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5769             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5770             0, 0, 0,
5771             paintbucket_offset_changed, 1, 2);
5772         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5774         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5775     }
5777     /* Auto Gap */
5778     {
5779         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5781         GList* items = 0;
5782         gint count = 0;
5783         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5784         {
5785             GtkTreeIter iter;
5786             gtk_list_store_append( model, &iter );
5787             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5788             count++;
5789         }
5790         g_list_free( items );
5791         items = 0;
5792         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5793         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5794         ege_select_one_action_set_appearance( act2, "compact" );
5795         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5796         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5797         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5798         g_object_set_data( holder, "autogap_action", act2 );
5799     }
5801     /* Reset */
5802     {
5803         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5804                                           _("Defaults"),
5805                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5806                                           GTK_STOCK_CLEAR );
5807         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5808         gtk_action_group_add_action( mainActions, act );
5809         gtk_action_set_sensitive( act, TRUE );
5810     }
5814 /*
5815   Local Variables:
5816   mode:c++
5817   c-file-style:"stroustrup"
5818   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5819   indent-tabs-mode:nil
5820   fill-column:99
5821   End:
5822 */
5823 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :