Code

647d95d800f837004abab5fa6350137ab0ecc53e
[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"
93 #include "interface.h"
94 #include "shortcuts.h"
96 #include "mod360.h"
98 #include "toolbox.h"
100 #include "flood-context.h"
102 #include "ink-action.h"
103 #include "ege-adjustment-action.h"
104 #include "ege-output-action.h"
105 #include "ege-select-one-action.h"
106 #include "helper/unit-tracker.h"
108 #include "svg/css-ostringstream.h"
110 #include "widgets/calligraphic-profile-rename.h"
112 using Inkscape::UnitTracker;
114 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
115 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
117 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
130 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
137 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
138     static Inkscape::IconSize sizeChoices[] = {
139         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
140         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
141         Inkscape::ICON_SIZE_MENU
142     };
143     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
144     return sizeChoices[index];
147 static struct {
148     gchar const *type_name;
149     gchar const *data_name;
150     sp_verb_t verb;
151     sp_verb_t doubleclick_verb;
152 } const tools[] = {
153     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
154     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
155     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
156     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
157     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
158     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
159     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
160     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
161     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
162     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
163     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
164     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
165     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
166     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
167     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
168     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
169     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
170     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
171     { NULL, NULL, 0, 0 }
172 };
174 static struct {
175     gchar const *type_name;
176     gchar const *data_name;
177     GtkWidget *(*create_func)(SPDesktop *desktop);
178     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
179     gchar const *ui_name;
180     gint swatch_verb_id;
181     gchar const *swatch_tool;
182     gchar const *swatch_tip;
183 } const aux_toolboxes[] = {
184     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
187       SP_VERB_INVALID, 0, 0},
188     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
189       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
190     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
191       SP_VERB_INVALID, 0, 0},
192     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
193       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
194     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
195       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
196     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
197       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
198     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
199       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
200     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
201       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
202     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
203       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
204     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
205       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
206     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
207       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
208     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
209       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
210     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
211       SP_VERB_INVALID, 0, 0},
212     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
213       SP_VERB_INVALID, 0, 0},
214     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
215       SP_VERB_INVALID, 0, 0},
216     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
217       SP_VERB_INVALID, 0, 0},
218     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
219       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
220     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
221 };
224 static gchar const * ui_descr =
225         "<ui>"
226         "  <toolbar name='SelectToolbar'>"
227         "    <toolitem action='EditSelectAll' />"
228         "    <toolitem action='EditSelectAllInAllLayers' />"
229         "    <toolitem action='EditDeselect' />"
230         "    <separator />"
231         "    <toolitem action='ObjectRotate90CCW' />"
232         "    <toolitem action='ObjectRotate90' />"
233         "    <toolitem action='ObjectFlipHorizontally' />"
234         "    <toolitem action='ObjectFlipVertically' />"
235         "    <separator />"
236         "    <toolitem action='SelectionToBack' />"
237         "    <toolitem action='SelectionLower' />"
238         "    <toolitem action='SelectionRaise' />"
239         "    <toolitem action='SelectionToFront' />"
240         "    <separator />"
241         "    <toolitem action='XAction' />"
242         "    <toolitem action='YAction' />"
243         "    <toolitem action='WidthAction' />"
244         "    <toolitem action='LockAction' />"
245         "    <toolitem action='HeightAction' />"
246         "    <toolitem action='UnitsAction' />"
247         "    <separator />"
248         "    <toolitem action='transform_affect_label' />"
249         "    <toolitem action='transform_stroke' />"
250         "    <toolitem action='transform_corners' />"
251         "    <toolitem action='transform_gradient' />"
252         "    <toolitem action='transform_pattern' />"
253         "  </toolbar>"
255         "  <toolbar name='NodeToolbar'>"
256         "    <toolitem action='NodeInsertAction' />"
257         "    <toolitem action='NodeDeleteAction' />"
258         "    <separator />"
259         "    <toolitem action='NodeJoinAction' />"
260         "    <toolitem action='NodeBreakAction' />"
261         "    <separator />"
262         "    <toolitem action='NodeJoinSegmentAction' />"
263         "    <toolitem action='NodeDeleteSegmentAction' />"
264         "    <separator />"
265         "    <toolitem action='NodeCuspAction' />"
266         "    <toolitem action='NodeSmoothAction' />"
267         "    <toolitem action='NodeSymmetricAction' />"
268         "    <separator />"
269         "    <toolitem action='NodeLineAction' />"
270         "    <toolitem action='NodeCurveAction' />"
271         "    <separator />"
272         "    <toolitem action='ObjectToPath' />"
273         "    <toolitem action='StrokeToPath' />"
274         "    <separator />"
275         "    <toolitem action='NodeXAction' />"
276         "    <toolitem action='NodeYAction' />"
277         "    <toolitem action='NodeUnitsAction' />"
278         "    <separator />"
279         "    <toolitem action='ObjectEditClipPathAction' />"
280         "    <toolitem action='ObjectEditMaskPathAction' />"
281         "    <toolitem action='EditNextLPEParameterAction' />"
282         "    <separator />"
283         "    <toolitem action='NodesShowHandlesAction' />"
284         "    <toolitem action='NodesShowHelperpath' />"
285         "  </toolbar>"
287         "  <toolbar name='TweakToolbar'>"
288         "    <toolitem action='TweakWidthAction' />"
289         "    <separator />"
290         "    <toolitem action='TweakForceAction' />"
291         "    <toolitem action='TweakPressureAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakModeAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakFidelityAction' />"
296         "    <separator />"
297         "    <toolitem action='TweakChannelsLabel' />"
298         "    <toolitem action='TweakDoH' />"
299         "    <toolitem action='TweakDoS' />"
300         "    <toolitem action='TweakDoL' />"
301         "    <toolitem action='TweakDoO' />"
302         "  </toolbar>"
304         "  <toolbar name='ZoomToolbar'>"
305         "    <toolitem action='ZoomIn' />"
306         "    <toolitem action='ZoomOut' />"
307         "    <separator />"
308         "    <toolitem action='Zoom1:0' />"
309         "    <toolitem action='Zoom1:2' />"
310         "    <toolitem action='Zoom2:1' />"
311         "    <separator />"
312         "    <toolitem action='ZoomSelection' />"
313         "    <toolitem action='ZoomDrawing' />"
314         "    <toolitem action='ZoomPage' />"
315         "    <toolitem action='ZoomPageWidth' />"
316         "    <separator />"
317         "    <toolitem action='ZoomPrev' />"
318         "    <toolitem action='ZoomNext' />"
319         "  </toolbar>"
321         "  <toolbar name='StarToolbar'>"
322         "    <separator />"
323         "    <toolitem action='StarStateAction' />"
324         "    <separator />"
325         "    <toolitem action='FlatAction' />"
326         "    <separator />"
327         "    <toolitem action='MagnitudeAction' />"
328         "    <toolitem action='SpokeAction' />"
329         "    <toolitem action='RoundednessAction' />"
330         "    <toolitem action='RandomizationAction' />"
331         "    <separator />"
332         "    <toolitem action='StarResetAction' />"
333         "  </toolbar>"
335         "  <toolbar name='RectToolbar'>"
336         "    <toolitem action='RectStateAction' />"
337         "    <toolitem action='RectWidthAction' />"
338         "    <toolitem action='RectHeightAction' />"
339         "    <toolitem action='RadiusXAction' />"
340         "    <toolitem action='RadiusYAction' />"
341         "    <toolitem action='RectUnitsAction' />"
342         "    <separator />"
343         "    <toolitem action='RectResetAction' />"
344         "  </toolbar>"
346         "  <toolbar name='3DBoxToolbar'>"
347         "    <toolitem action='3DBoxAngleXAction' />"
348         "    <toolitem action='3DBoxVPXStateAction' />"
349         "    <separator />"
350         "    <toolitem action='3DBoxAngleYAction' />"
351         "    <toolitem action='3DBoxVPYStateAction' />"
352         "    <separator />"
353         "    <toolitem action='3DBoxAngleZAction' />"
354         "    <toolitem action='3DBoxVPZStateAction' />"
355         "  </toolbar>"
357         "  <toolbar name='SpiralToolbar'>"
358         "    <toolitem action='SpiralStateAction' />"
359         "    <toolitem action='SpiralRevolutionAction' />"
360         "    <toolitem action='SpiralExpansionAction' />"
361         "    <toolitem action='SpiralT0Action' />"
362         "    <separator />"
363         "    <toolitem action='SpiralResetAction' />"
364         "  </toolbar>"
366         "  <toolbar name='PenToolbar'>"
367         "    <toolitem action='FreehandModeActionPenTemp' />"
368         "    <toolitem action='FreehandModeActionPen' />"
369         "  </toolbar>"
371         "  <toolbar name='PencilToolbar'>"
372         "    <toolitem action='FreehandModeActionPencilTemp' />"
373         "    <toolitem action='FreehandModeActionPencil' />"
374         "    <separator />"
375         "    <toolitem action='PencilToleranceAction' />"    
376         "    <separator />"
377         "    <toolitem action='PencilResetAction' />"
378         "  </toolbar>"
380         "  <toolbar name='CalligraphyToolbar'>"
381         "    <separator />"
382         "    <toolitem action='SetProfileAction'/>"
383         "    <toolitem action='SaveDeleteProfileAction'/>"
384         "    <separator />"
385         "    <toolitem action='CalligraphyWidthAction' />"
386         "    <toolitem action='PressureAction' />"
387         "    <toolitem action='TraceAction' />"
388         "    <toolitem action='ThinningAction' />"
389         "    <separator />"
390         "    <toolitem action='AngleAction' />"
391         "    <toolitem action='TiltAction' />"
392         "    <toolitem action='FixationAction' />"
393         "    <separator />"
394         "    <toolitem action='CapRoundingAction' />"
395         "    <separator />"
396         "    <toolitem action='TremorAction' />"
397         "    <toolitem action='WiggleAction' />"
398         "    <toolitem action='MassAction' />"
399         "    <separator />"
400         "  </toolbar>"
402         "  <toolbar name='ArcToolbar'>"
403         "    <toolitem action='ArcStateAction' />"
404         "    <separator />"
405         "    <toolitem action='ArcStartAction' />"
406         "    <toolitem action='ArcEndAction' />"
407         "    <separator />"
408         "    <toolitem action='ArcOpenAction' />"
409         "    <separator />"
410         "    <toolitem action='ArcResetAction' />"
411         "    <separator />"
412         "  </toolbar>"
414         "  <toolbar name='PaintbucketToolbar'>"
415         "    <toolitem action='ChannelsAction' />"
416         "    <separator />"
417         "    <toolitem action='ThresholdAction' />"
418         "    <separator />"
419         "    <toolitem action='OffsetAction' />"
420         "    <toolitem action='PaintbucketUnitsAction' />"
421         "    <separator />"
422         "    <toolitem action='AutoGapAction' />"
423         "    <separator />"
424         "    <toolitem action='PaintbucketResetAction' />"
425         "  </toolbar>"
427         "  <toolbar name='EraserToolbar'>"
428         "    <toolitem action='EraserWidthAction' />"
429         "    <separator />"
430         "    <toolitem action='EraserModeAction' />"
431         "  </toolbar>"
433         "  <toolbar name='DropperToolbar'>"
434         "    <toolitem action='DropperOpacityAction' />"
435         "    <toolitem action='DropperPickAlphaAction' />"
436         "    <toolitem action='DropperSetAlphaAction' />"
437         "  </toolbar>"
439         "  <toolbar name='ConnectorToolbar'>"
440         "    <toolitem action='ConnectorAvoidAction' />"
441         "    <toolitem action='ConnectorIgnoreAction' />"
442         "    <toolitem action='ConnectorSpacingAction' />"
443         "    <toolitem action='ConnectorGraphAction' />"
444         "    <toolitem action='ConnectorLengthAction' />"
445         "    <toolitem action='ConnectorDirectedAction' />"
446         "    <toolitem action='ConnectorOverlapAction' />"
447         "  </toolbar>"
449         "</ui>"
452 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
454 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
456 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
457 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
459 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
460 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
462 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
463 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
465 /* Global text entry widgets necessary for update */
466 /* GtkWidget *dropper_rgb_entry,
467           *dropper_opacity_entry ; */
468 // should be made a private member once this is converted to class
470 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
471     connection->disconnect();
472     delete connection;
475 static void purge_repr_listener( GObject* obj, GObject* tbl )
477     (void)obj;
478     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
479     if (oldrepr) { // remove old listener
480         sp_repr_remove_listener_by_data(oldrepr, tbl);
481         Inkscape::GC::release(oldrepr);
482         oldrepr = 0;
483         g_object_set_data( tbl, "repr", NULL );
484     }
487 GtkWidget *
488 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
489                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
490                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
492     SPAction *action = verb->get_action(view);
493     if (!action) return NULL;
495     SPAction *doubleclick_action;
496     if (doubleclick_verb)
497         doubleclick_action = doubleclick_verb->get_action(view);
498     else
499         doubleclick_action = NULL;
501     /* fixme: Handle sensitive/unsensitive */
502     /* fixme: Implement sp_button_new_from_action */
503     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
504     gtk_widget_show(b);
507     unsigned int shortcut = sp_shortcut_get_primary(verb);
508     if (shortcut) {
509         gchar key[256];
510         sp_ui_shortcut_string(shortcut, key);
511         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
512         gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
513         g_free(tip);
514     } else {
515         gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
516     }
518     return b;
522 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
524     SPAction* targetAction = SP_ACTION(user_data);
525     if ( targetAction ) {
526         sp_action_perform( targetAction, NULL );
527     }
530 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
532     if ( data ) {
533         GtkAction* act = GTK_ACTION(data);
534         gtk_action_set_sensitive( act, sensitive );
535     }
538 static SPActionEventVector action_event_vector = {
539     {NULL},
540     NULL,
541     NULL,
542     sp_action_action_set_sensitive,
543     NULL,
544     NULL
545 };
547 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
549     GtkAction* act = 0;
551     SPAction* targetAction = verb->get_action(view);
552     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
553     act = GTK_ACTION(inky);
554     gtk_action_set_sensitive( act, targetAction->sensitive );
556     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
558     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
559     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
561     return act;
564 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
566     Inkscape::UI::View::View *view = desktop;
567     gint verbsToUse[] = {
568         // disabled until we have icons for them:
569         //find
570         //SP_VERB_EDIT_TILE,
571         //SP_VERB_EDIT_UNTILE,
572         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
573         SP_VERB_DIALOG_DISPLAY,
574         SP_VERB_DIALOG_FILL_STROKE,
575         SP_VERB_DIALOG_NAMEDVIEW,
576         SP_VERB_DIALOG_TEXT,
577         SP_VERB_DIALOG_XML_EDITOR,
578         SP_VERB_EDIT_CLONE,
579         SP_VERB_EDIT_COPY,
580         SP_VERB_EDIT_CUT,
581         SP_VERB_EDIT_DUPLICATE,
582         SP_VERB_EDIT_PASTE,
583         SP_VERB_EDIT_REDO,
584         SP_VERB_EDIT_UNDO,
585         SP_VERB_EDIT_UNLINK_CLONE,
586         SP_VERB_FILE_EXPORT,
587         SP_VERB_FILE_IMPORT,
588         SP_VERB_FILE_NEW,
589         SP_VERB_FILE_OPEN,
590         SP_VERB_FILE_PRINT,
591         SP_VERB_FILE_SAVE,
592         SP_VERB_OBJECT_TO_CURVE,
593         SP_VERB_SELECTION_GROUP,
594         SP_VERB_SELECTION_OUTLINE,
595         SP_VERB_SELECTION_UNGROUP,
596         SP_VERB_ZOOM_1_1,
597         SP_VERB_ZOOM_1_2,
598         SP_VERB_ZOOM_2_1,
599         SP_VERB_ZOOM_DRAWING,
600         SP_VERB_ZOOM_IN,
601         SP_VERB_ZOOM_NEXT,
602         SP_VERB_ZOOM_OUT,
603         SP_VERB_ZOOM_PAGE,
604         SP_VERB_ZOOM_PAGE_WIDTH,
605         SP_VERB_ZOOM_PREV,
606         SP_VERB_ZOOM_SELECTION,
607     };
609     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
611     static std::map<SPDesktop*, GtkActionGroup*> groups;
612     GtkActionGroup* mainActions = 0;
613     if ( groups.find(desktop) != groups.end() ) {
614         mainActions = groups[desktop];
615     }
617     if ( !mainActions ) {
618         mainActions = gtk_action_group_new("main");
619         groups[desktop] = mainActions;
620     }
622     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
623         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
624         if ( verb ) {
625             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
626                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
627                 gtk_action_group_add_action( mainActions, act );
628             }
629         }
630     }
632     return mainActions;
636 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
638     gtk_widget_set_size_request( widget,
639                                  widget->allocation.width,
640                                  widget->allocation.height );
643 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
645     gtk_widget_set_size_request( widget, -1, -1 );
650 GtkWidget *
651 sp_tool_toolbox_new()
653     GtkTooltips *tt = gtk_tooltips_new();
654     GtkWidget* tb = gtk_toolbar_new();
655     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
656     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
658     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
659     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
661     gtk_widget_set_sensitive(tb, FALSE);
663     GtkWidget *hb = gtk_handle_box_new();
664     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
665     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
666     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
668      gtk_container_add(GTK_CONTAINER(hb), tb);
669      gtk_widget_show(GTK_WIDGET(tb));
671     sigc::connection* conn = new sigc::connection;
672      g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
674     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
675     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
677     return hb;
680 GtkWidget *
681 sp_aux_toolbox_new()
683     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
685     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
687     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
689     gtk_widget_set_sensitive(tb, FALSE);
691     GtkWidget *hb = gtk_handle_box_new();
692     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
693     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
694     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
696     gtk_container_add(GTK_CONTAINER(hb), tb);
697     gtk_widget_show(GTK_WIDGET(tb));
699     sigc::connection* conn = new sigc::connection;
700     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
702     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
703     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
705     return hb;
708 //####################################
709 //# Commands Bar
710 //####################################
712 GtkWidget *
713 sp_commands_toolbox_new()
715     GtkWidget *tb = gtk_toolbar_new();
717     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
718     gtk_widget_set_sensitive(tb, FALSE);
720     GtkWidget *hb = gtk_handle_box_new();
721     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
722     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
723     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
725     gtk_container_add(GTK_CONTAINER(hb), tb);
726     gtk_widget_show(GTK_WIDGET(tb));
728     sigc::connection* conn = new sigc::connection;
729     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
731     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
732     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
734     return hb;
738 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
739                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
740                                                        gchar const *path, gchar const *data, gdouble def,
741                                                        GtkWidget *focusTarget,
742                                                        GtkWidget *us,
743                                                        GObject *dataKludge,
744                                                        gboolean altx, gchar const *altx_mark,
745                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
746                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
747                                                        void (*callback)(GtkAdjustment *, GObject *),
748                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
750     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
751                                                              lower, upper, step, page, page ) );
752     if (us) {
753         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
754     }
756     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
758     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
759     if ( shortLabel ) {
760         g_object_set( act, "short_label", shortLabel, NULL );
761     }
763     if ( (descrCount > 0) && descrLabels && descrValues ) {
764         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
765     }
767     if ( focusTarget ) {
768         ege_adjustment_action_set_focuswidget( act, focusTarget );
769     }
771     if ( altx && altx_mark ) {
772         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
773     }
775     if ( dataKludge ) {
776         g_object_set_data( dataKludge, data, adj );
777     }
779     // Using a cast just to make sure we pass in the right kind of function pointer
780     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
782     return act;
786 //####################################
787 //# node editing callbacks
788 //####################################
790 /**
791  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
792  */
793 static ShapeEditor *get_current_shape_editor()
795     if (!SP_ACTIVE_DESKTOP) {
796         return NULL;
797     }
799     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
801     if (!SP_IS_NODE_CONTEXT(event_context)) {
802         return NULL;
803     }
805     return SP_NODE_CONTEXT(event_context)->shape_editor;
809 void
810 sp_node_path_edit_add(void)
812     ShapeEditor *shape_editor = get_current_shape_editor();
813     if (shape_editor) shape_editor->add_node();
816 void
817 sp_node_path_edit_delete(void)
819     ShapeEditor *shape_editor = get_current_shape_editor();
820     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
823 void
824 sp_node_path_edit_delete_segment(void)
826     ShapeEditor *shape_editor = get_current_shape_editor();
827     if (shape_editor) shape_editor->delete_segment();
830 void
831 sp_node_path_edit_break(void)
833     ShapeEditor *shape_editor = get_current_shape_editor();
834     if (shape_editor) shape_editor->break_at_nodes();
837 void
838 sp_node_path_edit_join(void)
840     ShapeEditor *shape_editor = get_current_shape_editor();
841     if (shape_editor) shape_editor->join_nodes();
844 void
845 sp_node_path_edit_join_segment(void)
847     ShapeEditor *shape_editor = get_current_shape_editor();
848     if (shape_editor) shape_editor->join_segments();
851 void
852 sp_node_path_edit_toline(void)
854     ShapeEditor *shape_editor = get_current_shape_editor();
855     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
858 void
859 sp_node_path_edit_tocurve(void)
861     ShapeEditor *shape_editor = get_current_shape_editor();
862     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
865 void
866 sp_node_path_edit_cusp(void)
868     ShapeEditor *shape_editor = get_current_shape_editor();
869     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
872 void
873 sp_node_path_edit_smooth(void)
875     ShapeEditor *shape_editor = get_current_shape_editor();
876     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
879 void
880 sp_node_path_edit_symmetrical(void)
882     ShapeEditor *shape_editor = get_current_shape_editor();
883     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
886 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
887     bool show = gtk_toggle_action_get_active( act );
888     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
889     ShapeEditor *shape_editor = get_current_shape_editor();
890     if (shape_editor) shape_editor->show_handles(show);
893 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
894     bool show = gtk_toggle_action_get_active( act );
895     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
896     ShapeEditor *shape_editor = get_current_shape_editor();
897     if (shape_editor) shape_editor->show_helperpath(show);
900 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
901     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
904 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
905     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
908 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
909     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
912 /* is called when the node selection is modified */
913 static void
914 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
916     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
917     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
918     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
919     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
921     // quit if run by the attr_changed listener
922     if (g_object_get_data( tbl, "freeze" )) {
923         return;
924     }
926     // in turn, prevent listener from responding
927     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
929     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
930     SPUnit const *unit = tracker->getActiveUnit();
932     ShapeEditor *shape_editor = get_current_shape_editor();
933     if (shape_editor && shape_editor->has_nodepath()) {
934         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
935         int n_selected = 0;
936         if (nodepath) {
937             n_selected = nodepath->numSelected();
938         }
940         if (n_selected == 0) {
941             gtk_action_set_sensitive(xact, FALSE);
942             gtk_action_set_sensitive(yact, FALSE);
943         } else {
944             gtk_action_set_sensitive(xact, TRUE);
945             gtk_action_set_sensitive(yact, TRUE);
946             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
947             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
949             if (n_selected == 1) {
950                 NR::Point sel_node = nodepath->singleSelectedCoords();
951                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
952                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
953                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
954                 }
955             } else {
956                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
957                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
958                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
959                     /* Note: Currently x and y will always have a value, even if the coordinates of the
960                        selected nodes don't coincide (in this case we use the coordinates of the center
961                        of the bounding box). So the entries are never set to zero. */
962                     // FIXME: Maybe we should clear the entry if several nodes are selected
963                     //        instead of providing a kind of average value
964                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
965                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
966                 }
967             }
968         }
969     } else {
970         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
971         gtk_action_set_sensitive(xact, FALSE);
972         gtk_action_set_sensitive(yact, FALSE);
973     }
975     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
978 static void
979 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
981     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
983     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
984     SPUnit const *unit = tracker->getActiveUnit();
986     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
987         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
988     }
990     // quit if run by the attr_changed listener
991     if (g_object_get_data( tbl, "freeze" )) {
992         return;
993     }
995     // in turn, prevent listener from responding
996     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
998     ShapeEditor *shape_editor = get_current_shape_editor();
999     if (shape_editor && shape_editor->has_nodepath()) {
1000         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1001         if (!strcmp(value_name, "x")) {
1002             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1003         }
1004         if (!strcmp(value_name, "y")) {
1005             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1006         }
1007     }
1009     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1012 static void
1013 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1015     sp_node_path_value_changed(adj, tbl, "x");
1018 static void
1019 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1021     sp_node_path_value_changed(adj, tbl, "y");
1024 void
1025 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1027     {
1028     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1029     SPItem *item = selection->singleItem();
1030     if (item && SP_IS_LPE_ITEM(item)) {
1031        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1032            gtk_action_set_sensitive(w, TRUE);
1033        } else {
1034            gtk_action_set_sensitive(w, FALSE);
1035        }
1036     } else {
1037        gtk_action_set_sensitive(w, FALSE);
1038     }
1039     }
1041     {
1042     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1043     SPItem *item = selection->singleItem();
1044     if (item && item->clip_ref && item->clip_ref->getObject()) {
1045        gtk_action_set_sensitive(w, TRUE);
1046     } else {
1047        gtk_action_set_sensitive(w, FALSE);
1048     }
1049     }
1051     {
1052     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1053     SPItem *item = selection->singleItem();
1054     if (item && item->mask_ref && item->mask_ref->getObject()) {
1055        gtk_action_set_sensitive(w, TRUE);
1056     } else {
1057        gtk_action_set_sensitive(w, FALSE);
1058     }
1059     }
1062 void
1063 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1065     sp_node_toolbox_sel_changed (selection, tbl);
1070 //################################
1071 //##    Node Editing Toolbox    ##
1072 //################################
1074 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1076     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1077     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1078     g_object_set_data( holder, "tracker", tracker );
1080     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1082     {
1083         InkAction* inky = ink_action_new( "NodeInsertAction",
1084                                           _("Insert node"),
1085                                           _("Insert new nodes into selected segments"),
1086                                           "node_insert",
1087                                           secondarySize );
1088         g_object_set( inky, "short_label", _("Insert"), NULL );
1089         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1090         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1091     }
1093     {
1094         InkAction* inky = ink_action_new( "NodeDeleteAction",
1095                                           _("Delete node"),
1096                                           _("Delete selected nodes"),
1097                                           "node_delete",
1098                                           secondarySize );
1099         g_object_set( inky, "short_label", _("Delete"), NULL );
1100         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1101         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1102     }
1104     {
1105         InkAction* inky = ink_action_new( "NodeJoinAction",
1106                                           _("Join endnodes"),
1107                                           _("Join selected endnodes"),
1108                                           "node_join",
1109                                           secondarySize );
1110         g_object_set( inky, "short_label", _("Join"), NULL );
1111         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1112         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1113     }
1115     {
1116         InkAction* inky = ink_action_new( "NodeBreakAction",
1117                                           _("Break nodes"),
1118                                           _("Break path at selected nodes"),
1119                                           "node_break",
1120                                           secondarySize );
1121         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1122         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1123     }
1126     {
1127         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1128                                           _("Join with segment"),
1129                                           _("Join selected endnodes with a new segment"),
1130                                           "node_join_segment",
1131                                           secondarySize );
1132         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1133         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1134     }
1136     {
1137         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1138                                           _("Delete segment"),
1139                                           _("Delete segment between two non-endpoint nodes"),
1140                                           "node_delete_segment",
1141                                           secondarySize );
1142         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1143         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1144     }
1146     {
1147         InkAction* inky = ink_action_new( "NodeCuspAction",
1148                                           _("Node Cusp"),
1149                                           _("Make selected nodes corner"),
1150                                           "node_cusp",
1151                                           secondarySize );
1152         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1153         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1154     }
1156     {
1157         InkAction* inky = ink_action_new( "NodeSmoothAction",
1158                                           _("Node Smooth"),
1159                                           _("Make selected nodes smooth"),
1160                                           "node_smooth",
1161                                           secondarySize );
1162         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1163         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1164     }
1166     {
1167         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1168                                           _("Node Symmetric"),
1169                                           _("Make selected nodes symmetric"),
1170                                           "node_symmetric",
1171                                           secondarySize );
1172         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1173         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1174     }
1176     {
1177         InkAction* inky = ink_action_new( "NodeLineAction",
1178                                           _("Node Line"),
1179                                           _("Make selected segments lines"),
1180                                           "node_line",
1181                                           secondarySize );
1182         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1183         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1184     }
1186     {
1187         InkAction* inky = ink_action_new( "NodeCurveAction",
1188                                           _("Node Curve"),
1189                                           _("Make selected segments curves"),
1190                                           "node_curve",
1191                                           secondarySize );
1192         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1193         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1194     }
1196     {
1197         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1198                                                       _("Show Handles"),
1199                                                       _("Show the Bezier handles of selected nodes"),
1200                                                       "nodes_show_handles",
1201                                                       Inkscape::ICON_SIZE_DECORATION );
1202         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1203         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1204         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1205     }
1207     {
1208         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1209                                                       _("Show Outline"),
1210                                                       _("Show the outline of the path"),
1211                                                       "nodes_show_helperpath",
1212                                                       Inkscape::ICON_SIZE_DECORATION );
1213         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1214         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1215         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1216     }
1218     {
1219         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1220                                           _("Next path effect parameter"),
1221                                           _("Show next path effect parameter for editing"),
1222                                           "edit_next_parameter",
1223                                           Inkscape::ICON_SIZE_DECORATION );
1224         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1225         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1226         g_object_set_data( holder, "nodes_lpeedit", inky);
1227     }
1229     {
1230         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1231                                           _("Edit clipping path"),
1232                                           _("Edit the clipping path of the object"),
1233                                           "nodeedit-clippath",
1234                                           Inkscape::ICON_SIZE_DECORATION );
1235         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1236         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1237         g_object_set_data( holder, "nodes_clippathedit", inky);
1238     }
1240     {
1241         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1242                                           _("Edit mask path"),
1243                                           _("Edit the mask of the object"),
1244                                           "nodeedit-mask",
1245                                           Inkscape::ICON_SIZE_DECORATION );
1246         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1247         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1248         g_object_set_data( holder, "nodes_maskedit", inky);
1249     }
1251     /* X coord of selected node(s) */
1252     {
1253         EgeAdjustmentAction* eact = 0;
1254         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1255         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1256         eact = create_adjustment_action( "NodeXAction",
1257                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1258                                          "tools.nodes", "Xcoord", 0,
1259                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1260                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1261                                          labels, values, G_N_ELEMENTS(labels),
1262                                          sp_node_path_x_value_changed );
1263         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1264         g_object_set_data( holder, "nodes_x_action", eact );
1265         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1266         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1267     }
1269     /* Y coord of selected node(s) */
1270     {
1271         EgeAdjustmentAction* eact = 0;
1272         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1273         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1274         eact = create_adjustment_action( "NodeYAction",
1275                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1276                                          "tools.nodes", "Ycoord", 0,
1277                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1278                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1279                                          labels, values, G_N_ELEMENTS(labels),
1280                                          sp_node_path_y_value_changed );
1281         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1282         g_object_set_data( holder, "nodes_y_action", eact );
1283         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1284         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1285     }
1287     // add the units menu
1288     {
1289         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1290         gtk_action_group_add_action( mainActions, act );
1291     }
1294     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1296     //watch selection
1297     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1299     sigc::connection *c_selection_changed =
1300         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1301                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1302     pool->add_connection ("selection-changed", c_selection_changed);
1304     sigc::connection *c_selection_modified =
1305         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1306                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1307     pool->add_connection ("selection-modified", c_selection_modified);
1309     sigc::connection *c_subselection_changed =
1310         new sigc::connection (desktop->connectToolSubselectionChanged
1311                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1312     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1314     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1316     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1317 } // end of sp_node_toolbox_prep()
1320 //########################
1321 //##    Zoom Toolbox    ##
1322 //########################
1324 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1326     // no custom GtkAction setup needed
1327 } // end of sp_zoom_toolbox_prep()
1329 void
1330 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1332     if ( GTK_IS_BIN(toolbox) ) {
1333         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")));
1334     } else if (GTK_IS_TOOLBAR(toolbox) ) {
1335         toolbox_set_desktop(toolbox,
1336                             desktop,
1337                             setup_tool_toolbox,
1338                             update_tool_toolbox,
1339                             static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1340                                                                              "event_context_connection")));
1341     } else {
1342         g_warning("Unexpected toolbox type");
1343     }
1347 void
1348 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1350     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1351                         desktop,
1352                         setup_aux_toolbox,
1353                         update_aux_toolbox,
1354                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1355                                                                          "event_context_connection")));
1358 void
1359 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1361     toolbox_set_desktop(toolbox,
1362                         desktop,
1363                         setup_commands_toolbox,
1364                         update_commands_toolbox,
1365                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1366                                                                          "event_context_connection")));
1369 static void
1370 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1372     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1373     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1375     if (old_desktop) {
1376         GList *children, *iter;
1378         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1379         for ( iter = children ; iter ; iter = iter->next ) {
1380             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1381         }
1382         g_list_free(children);
1383     }
1385     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1387     if (desktop) {
1388         gtk_widget_set_sensitive(toolbox, TRUE);
1389         setup_func(toolbox, desktop);
1390         update_func(desktop, desktop->event_context, toolbox);
1391         *conn = desktop->connectEventContextChanged
1392             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1393     } else {
1394         gtk_widget_set_sensitive(toolbox, FALSE);
1395     }
1397 } // end of toolbox_set_desktop()
1400 static void
1401 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1403     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1404     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1406     for (int i = 0 ; tools[i].type_name ; i++ ) {
1407         GtkWidget *button =
1408             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1409                                                               SP_BUTTON_TYPE_TOGGLE,
1410                                                               Inkscape::Verb::get(tools[i].verb),
1411                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1412                                                               desktop,
1413                                                               tooltips );
1415         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1416                            (gpointer)button );
1417     }
1421 static void
1422 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1424     gchar const *const tname = ( eventcontext
1425                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1426                                  : NULL );
1427     for (int i = 0 ; tools[i].type_name ; i++ ) {
1428         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1429         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1430     }
1433 static void
1434 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1436     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1437     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1438     GtkUIManager* mgr = gtk_ui_manager_new();
1439     GError* errVal = 0;
1440     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1441     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1443     std::map<std::string, GtkWidget*> dataHolders;
1445     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1446         if ( aux_toolboxes[i].prep_func ) {
1447             // converted to GtkActions and UIManager
1449             GtkWidget* kludge = gtk_toolbar_new();
1450             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1451             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1452             dataHolders[aux_toolboxes[i].type_name] = kludge;
1453             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1454         } else {
1456             GtkWidget *sub_toolbox = 0;
1457             if (aux_toolboxes[i].create_func == NULL)
1458                 sub_toolbox = sp_empty_toolbox_new(desktop);
1459             else {
1460                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1461             }
1463             gtk_size_group_add_widget( grouper, sub_toolbox );
1465             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1466             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1468         }
1469     }
1471     // Second pass to create toolbars *after* all GtkActions are created
1472     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1473         if ( aux_toolboxes[i].prep_func ) {
1474             // converted to GtkActions and UIManager
1476             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1478             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1479             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1481             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1482             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1483             g_free( tmp );
1484             tmp = 0;
1486             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1487             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1488                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1489             }
1490             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1493             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1495             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1496                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1497                 swatch->setDesktop( desktop );
1498                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1499                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1500                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1501                 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 );
1502             }
1504             gtk_widget_show_all( holder );
1505             sp_set_font_size_smaller( holder );
1507             gtk_size_group_add_widget( grouper, holder );
1509             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1510             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1511         }
1512     }
1514     g_object_unref( G_OBJECT(grouper) );
1517 static void
1518 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1520     gchar const *tname = ( eventcontext
1521                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1522                            : NULL );
1523     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1524         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1525         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1526             gtk_widget_show_all(sub_toolbox);
1527             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1528         } else {
1529             gtk_widget_hide(sub_toolbox);
1530         }
1531     }
1534 static void
1535 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1537     gchar const * descr =
1538         "<ui>"
1539         "  <toolbar name='CommandsToolbar'>"
1540         "    <toolitem action='FileNew' />"
1541         "    <toolitem action='FileOpen' />"
1542         "    <toolitem action='FileSave' />"
1543         "    <toolitem action='FilePrint' />"
1544         "    <separator />"
1545         "    <toolitem action='FileImport' />"
1546         "    <toolitem action='FileExport' />"
1547         "    <separator />"
1548         "    <toolitem action='EditUndo' />"
1549         "    <toolitem action='EditRedo' />"
1550         "    <separator />"
1551         "    <toolitem action='EditCopy' />"
1552         "    <toolitem action='EditCut' />"
1553         "    <toolitem action='EditPaste' />"
1554         "    <separator />"
1555         "    <toolitem action='ZoomSelection' />"
1556         "    <toolitem action='ZoomDrawing' />"
1557         "    <toolitem action='ZoomPage' />"
1558         "    <separator />"
1559         "    <toolitem action='EditDuplicate' />"
1560         "    <toolitem action='EditClone' />"
1561         "    <toolitem action='EditUnlinkClone' />"
1562         "    <separator />"
1563         "    <toolitem action='SelectionGroup' />"
1564         "    <toolitem action='SelectionUnGroup' />"
1565         "    <separator />"
1566         "    <toolitem action='DialogFillStroke' />"
1567         "    <toolitem action='DialogText' />"
1568         "    <toolitem action='DialogXMLEditor' />"
1569         "    <toolitem action='DialogAlignDistribute' />"
1570         "    <separator />"
1571         "    <toolitem action='DialogPreferences' />"
1572         "    <toolitem action='DialogDocumentProperties' />"
1573         "  </toolbar>"
1574         "</ui>";
1575     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1578     GtkUIManager* mgr = gtk_ui_manager_new();
1579     GError* errVal = 0;
1581     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1582     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1584     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1585     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1586         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1587     }
1589     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1590     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1592     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1593     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1596     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1598     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1599     if ( child ) {
1600         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1601     }
1603     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1606 static void
1607 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1611 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1613     gtk_widget_show(toolbox_toplevel);
1614     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1616     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1617     if (!shown_toolbox) {
1618         return;
1619     }
1620     gtk_widget_show(toolbox);
1622     gtk_widget_show_all(shown_toolbox);
1625 static GtkWidget *
1626 sp_empty_toolbox_new(SPDesktop *desktop)
1628     GtkWidget *tbl = gtk_toolbar_new();
1629     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1630     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1632     gtk_widget_show_all(tbl);
1633     sp_set_font_size_smaller (tbl);
1635     return tbl;
1638 // helper UI functions
1640 GtkWidget *
1641 sp_tb_spinbutton(
1642     gchar *label, gchar const *tooltip,
1643     gchar const *path, gchar const *data, gdouble def,
1644     GtkWidget *us,
1645     GtkWidget *tbl,
1646     gboolean altx, gchar const *altx_mark,
1647     gdouble lower, gdouble upper, gdouble step, gdouble page,
1648     void (*callback)(GtkAdjustment *, GtkWidget *),
1649     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1651     GtkTooltips *tt = gtk_tooltips_new();
1653     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1655     GtkWidget *l = gtk_label_new(label);
1656     gtk_widget_show(l);
1657     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1658     gtk_container_add(GTK_CONTAINER(hb), l);
1660     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1661                                       lower, upper, step, page, page);
1662     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1663     if (us)
1664         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1666     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1667     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1668     if (altx)
1669         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1670     gtk_widget_set_size_request(sb,
1671                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1672                                 AUX_SPINBUTTON_HEIGHT);
1673     gtk_widget_show(sb);
1674     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1675     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1676     gtk_container_add(GTK_CONTAINER(hb), sb);
1677     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1679     return hb;
1682 #define MODE_LABEL_WIDTH 70
1684 //########################
1685 //##       Star         ##
1686 //########################
1688 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1690     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1692     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1693         // do not remember prefs if this call is initiated by an undo change, because undoing object
1694         // creation sets bogus values to its attributes before it is deleted
1695         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1696     }
1698     // quit if run by the attr_changed listener
1699     if (g_object_get_data( dataKludge, "freeze" )) {
1700         return;
1701     }
1703     // in turn, prevent listener from responding
1704     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1706     bool modmade = false;
1708     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1709     GSList const *items = selection->itemList();
1710     for (; items != NULL; items = items->next) {
1711         if (SP_IS_STAR((SPItem *) items->data)) {
1712             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1713             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1714             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1715                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1716                                     + M_PI / (gint)adj->value));
1717             SP_OBJECT((SPItem *) items->data)->updateRepr();
1718             modmade = true;
1719         }
1720     }
1721     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1722                                    _("Star: Change number of corners"));
1724     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1727 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1729     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1731     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1732         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1733     }
1735     // quit if run by the attr_changed listener
1736     if (g_object_get_data( dataKludge, "freeze" )) {
1737         return;
1738     }
1740     // in turn, prevent listener from responding
1741     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1743     bool modmade = false;
1744     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1745     GSList const *items = selection->itemList();
1746     for (; items != NULL; items = items->next) {
1747         if (SP_IS_STAR((SPItem *) items->data)) {
1748             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1750             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1751             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1752             if (r2 < r1) {
1753                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1754             } else {
1755                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1756             }
1758             SP_OBJECT((SPItem *) items->data)->updateRepr();
1759             modmade = true;
1760         }
1761     }
1763     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1764                                    _("Star: Change spoke ratio"));
1766     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1769 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1771     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1772     bool flat = ege_select_one_action_get_active( act ) == 0;
1774     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1775         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1776                                     flat ? "true" : "false" );
1777     }
1779     // quit if run by the attr_changed listener
1780     if (g_object_get_data( dataKludge, "freeze" )) {
1781         return;
1782     }
1784     // in turn, prevent listener from responding
1785     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1787     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1788     GSList const *items = selection->itemList();
1789     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1790     bool modmade = false;
1792     if ( prop_action ) {
1793         gtk_action_set_sensitive( prop_action, !flat );
1794     }
1796     for (; items != NULL; items = items->next) {
1797         if (SP_IS_STAR((SPItem *) items->data)) {
1798             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1799             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1800             SP_OBJECT((SPItem *) items->data)->updateRepr();
1801             modmade = true;
1802         }
1803     }
1805     if (modmade) {
1806         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1807                          flat ? _("Make polygon") : _("Make star"));
1808     }
1810     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1813 static void sp_stb_rounded_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", "rounded", (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:rounded", (gdouble) adj->value);
1837             SP_OBJECT(items->data)->updateRepr();
1838             modmade = true;
1839         }
1840     }
1841     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1842                                    _("Star: Change rounding"));
1844     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1847 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1849     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1851     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1852         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1853     }
1855     // quit if run by the attr_changed listener
1856     if (g_object_get_data( dataKludge, "freeze" )) {
1857         return;
1858     }
1860     // in turn, prevent listener from responding
1861     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1863     bool modmade = false;
1865     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1866     GSList const *items = selection->itemList();
1867     for (; items != NULL; items = items->next) {
1868         if (SP_IS_STAR((SPItem *) items->data)) {
1869             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1870             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1871             SP_OBJECT(items->data)->updateRepr();
1872             modmade = true;
1873         }
1874     }
1875     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1876                                    _("Star: Change randomization"));
1878     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1882 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1883                                        gchar const */*old_value*/, gchar const */*new_value*/,
1884                                        bool /*is_interactive*/, gpointer data)
1886     GtkWidget *tbl = GTK_WIDGET(data);
1888     // quit if run by the _changed callbacks
1889     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1890         return;
1891     }
1893     // in turn, prevent callbacks from responding
1894     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1896     GtkAdjustment *adj = 0;
1898     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1899     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1901     if (!strcmp(name, "inkscape:randomized")) {
1902         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1903         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1904     } else if (!strcmp(name, "inkscape:rounded")) {
1905         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1906         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1907     } else if (!strcmp(name, "inkscape:flatsided")) {
1908         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1909         char const *flatsides = repr->attribute("inkscape:flatsided");
1910         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1911         if ( flatsides && !strcmp(flatsides,"false") ) {
1912             ege_select_one_action_set_active( flat_action, 1 );
1913             gtk_action_set_sensitive( prop_action, TRUE );
1914         } else {
1915             ege_select_one_action_set_active( flat_action, 0 );
1916             gtk_action_set_sensitive( prop_action, FALSE );
1917         }
1918     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1919         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1920         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1921         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1922         if (r2 < r1) {
1923             gtk_adjustment_set_value(adj, r2/r1);
1924         } else {
1925             gtk_adjustment_set_value(adj, r1/r2);
1926         }
1927     } else if (!strcmp(name, "sodipodi:sides")) {
1928         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1929         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1930     }
1932     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1936 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1938     NULL, /* child_added */
1939     NULL, /* child_removed */
1940     star_tb_event_attr_changed,
1941     NULL, /* content_changed */
1942     NULL  /* order_changed */
1943 };
1946 /**
1947  *  \param selection Should not be NULL.
1948  */
1949 static void
1950 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1952     int n_selected = 0;
1953     Inkscape::XML::Node *repr = NULL;
1955     purge_repr_listener( tbl, tbl );
1957     for (GSList const *items = selection->itemList();
1958          items != NULL;
1959          items = items->next)
1960     {
1961         if (SP_IS_STAR((SPItem *) items->data)) {
1962             n_selected++;
1963             repr = SP_OBJECT_REPR((SPItem *) items->data);
1964         }
1965     }
1967     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1969     if (n_selected == 0) {
1970         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1971     } else if (n_selected == 1) {
1972         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1974         if (repr) {
1975             g_object_set_data( tbl, "repr", repr );
1976             Inkscape::GC::anchor(repr);
1977             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1978             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1979         }
1980     } else {
1981         // FIXME: implement averaging of all parameters for multiple selected stars
1982         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1983         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1984     }
1988 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1990     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1991     // callbacks to lump all the changes for all selected objects in one undo step
1993     GtkAdjustment *adj = 0;
1995     // fixme: make settable in prefs!
1996     gint mag = 5;
1997     gdouble prop = 0.5;
1998     gboolean flat = FALSE;
1999     gdouble randomized = 0;
2000     gdouble rounded = 0;
2002     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2003     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2005     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2006     gtk_action_set_sensitive( sb2, !flat );
2008     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2009     gtk_adjustment_set_value(adj, mag);
2010     gtk_adjustment_value_changed(adj);
2012     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2013     gtk_adjustment_set_value(adj, prop);
2014     gtk_adjustment_value_changed(adj);
2016     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2017     gtk_adjustment_set_value(adj, rounded);
2018     gtk_adjustment_value_changed(adj);
2020     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2021     gtk_adjustment_set_value(adj, randomized);
2022     gtk_adjustment_value_changed(adj);
2026 void
2027 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2029     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2030     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2031     GtkWidget *l = gtk_label_new(NULL);
2032     gtk_label_set_markup(GTK_LABEL(l), title);
2033     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2034     if ( GTK_IS_TOOLBAR(tbl) ) {
2035         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2036     } else {
2037         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2038     }
2039     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2043 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2045     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2047     {
2048         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2049         ege_output_action_set_use_markup( act, TRUE );
2050         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2051         g_object_set_data( holder, "mode_action", act );
2052     }
2054     {
2055         EgeAdjustmentAction* eact = 0;
2056         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2057         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2059         /* Flatsided checkbox */
2060         {
2061             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2063             GtkTreeIter iter;
2064             gtk_list_store_append( model, &iter );
2065             gtk_list_store_set( model, &iter,
2066                                 0, _("Polygon"),
2067                                 1, _("Regular polygon (with one handle) instead of a star"),
2068                                 2, "star_flat",
2069                                 -1 );
2071             gtk_list_store_append( model, &iter );
2072             gtk_list_store_set( model, &iter,
2073                                 0, _("Star"),
2074                                 1, _("Star instead of a regular polygon (with one handle)"),
2075                                 2, "star_angled",
2076                                 -1 );
2078             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2079             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2080             g_object_set_data( holder, "flat_action", act );
2082             ege_select_one_action_set_appearance( act, "full" );
2083             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2084             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2085             ege_select_one_action_set_icon_column( act, 2 );
2086             ege_select_one_action_set_icon_size( act, secondarySize );
2087             ege_select_one_action_set_tooltip_column( act, 1  );
2089             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2090             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2091         }
2093         /* Magnitude */
2094         {
2095         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2096         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2097         eact = create_adjustment_action( "MagnitudeAction",
2098                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2099                                          "tools.shapes.star", "magnitude", 3,
2100                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2101                                          3, 1024, 1, 5,
2102                                          labels, values, G_N_ELEMENTS(labels),
2103                                          sp_stb_magnitude_value_changed,
2104                                          1.0, 0 );
2105         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2106         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2107         }
2109         /* Spoke ratio */
2110         {
2111         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2112         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2113         eact = create_adjustment_action( "SpokeAction",
2114                                          _("Spoke ratio"), _("Spoke ratio:"),
2115                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2116                                          // Base radius is the same for the closest handle.
2117                                          _("Base radius to tip radius ratio"),
2118                                          "tools.shapes.star", "proportion", 0.5,
2119                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2120                                          0.01, 1.0, 0.01, 0.1,
2121                                          labels, values, G_N_ELEMENTS(labels),
2122                                          sp_stb_proportion_value_changed );
2123         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2124         g_object_set_data( holder, "prop_action", eact );
2125         }
2127         if ( !isFlatSided ) {
2128             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2129         } else {
2130             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2131         }
2133         /* Roundedness */
2134         {
2135         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2136         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2137         eact = create_adjustment_action( "RoundednessAction",
2138                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2139                                          "tools.shapes.star", "rounded", 0.0,
2140                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2141                                          -10.0, 10.0, 0.01, 0.1,
2142                                          labels, values, G_N_ELEMENTS(labels),
2143                                          sp_stb_rounded_value_changed );
2144         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2145         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2146         }
2148         /* Randomization */
2149         {
2150         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2151         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2152         eact = create_adjustment_action( "RandomizationAction",
2153                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2154                                          "tools.shapes.star", "randomized", 0.0,
2155                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2156                                          -10.0, 10.0, 0.001, 0.01,
2157                                          labels, values, G_N_ELEMENTS(labels),
2158                                          sp_stb_randomized_value_changed, 0.1, 3 );
2159         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2160         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2161         }
2162     }
2164     {
2165         /* Reset */
2166         {
2167             GtkAction* act = gtk_action_new( "StarResetAction",
2168                                              _("Defaults"),
2169                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2170                                              GTK_STOCK_CLEAR );
2171             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2172             gtk_action_group_add_action( mainActions, act );
2173             gtk_action_set_sensitive( act, TRUE );
2174         }
2175     }
2177     sigc::connection *connection = new sigc::connection(
2178         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2179         );
2180     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2181     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2185 //########################
2186 //##       Rect         ##
2187 //########################
2189 static void sp_rtb_sensitivize( GObject *tbl )
2191     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2192     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2193     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2195     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2196         gtk_action_set_sensitive( not_rounded, FALSE );
2197     } else {
2198         gtk_action_set_sensitive( not_rounded, TRUE );
2199     }
2203 static void
2204 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2205                           void (*setter)(SPRect *, gdouble))
2207     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2209     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2210     SPUnit const *unit = tracker->getActiveUnit();
2212     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2213         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2214     }
2216     // quit if run by the attr_changed listener
2217     if (g_object_get_data( tbl, "freeze" )) {
2218         return;
2219     }
2221     // in turn, prevent listener from responding
2222     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2224     bool modmade = false;
2225     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2226     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2227         if (SP_IS_RECT(items->data)) {
2228             if (adj->value != 0) {
2229                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2230             } else {
2231                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2232             }
2233             modmade = true;
2234         }
2235     }
2237     sp_rtb_sensitivize( tbl );
2239     if (modmade) {
2240         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2241                                    _("Change rectangle"));
2242     }
2244     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2247 static void
2248 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2250     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2253 static void
2254 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2256     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2259 static void
2260 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2262     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2265 static void
2266 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2268     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2273 static void
2274 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2276     GtkAdjustment *adj = 0;
2278     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2279     gtk_adjustment_set_value(adj, 0.0);
2280     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2281     gtk_adjustment_value_changed(adj);
2283     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2284     gtk_adjustment_set_value(adj, 0.0);
2285     gtk_adjustment_value_changed(adj);
2287     sp_rtb_sensitivize( obj );
2290 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2291                                        gchar const */*old_value*/, gchar const */*new_value*/,
2292                                        bool /*is_interactive*/, gpointer data)
2294     GObject *tbl = G_OBJECT(data);
2296     // quit if run by the _changed callbacks
2297     if (g_object_get_data( tbl, "freeze" )) {
2298         return;
2299     }
2301     // in turn, prevent callbacks from responding
2302     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2304     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2305     SPUnit const *unit = tracker->getActiveUnit();
2307     gpointer item = g_object_get_data( tbl, "item" );
2308     if (item && SP_IS_RECT(item)) {
2309         {
2310             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2311             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2312             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2313         }
2315         {
2316             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2317             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2318             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2319         }
2321         {
2322             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2323             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2324             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2325         }
2327         {
2328             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2329             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2330             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2331         }
2332     }
2334     sp_rtb_sensitivize( tbl );
2336     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2340 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2341     NULL, /* child_added */
2342     NULL, /* child_removed */
2343     rect_tb_event_attr_changed,
2344     NULL, /* content_changed */
2345     NULL  /* order_changed */
2346 };
2348 /**
2349  *  \param selection should not be NULL.
2350  */
2351 static void
2352 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2354     int n_selected = 0;
2355     Inkscape::XML::Node *repr = NULL;
2356     SPItem *item = NULL;
2358     if ( g_object_get_data( tbl, "repr" ) ) {
2359         g_object_set_data( tbl, "item", NULL );
2360     }
2361     purge_repr_listener( tbl, tbl );
2363     for (GSList const *items = selection->itemList();
2364          items != NULL;
2365          items = items->next) {
2366         if (SP_IS_RECT((SPItem *) items->data)) {
2367             n_selected++;
2368             item = (SPItem *) items->data;
2369             repr = SP_OBJECT_REPR(item);
2370         }
2371     }
2373     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2375     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2377     if (n_selected == 0) {
2378         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2380         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2381         gtk_action_set_sensitive(w, FALSE);
2382         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2383         gtk_action_set_sensitive(h, FALSE);
2385     } else if (n_selected == 1) {
2386         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2387         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2389         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2390         gtk_action_set_sensitive(w, TRUE);
2391         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2392         gtk_action_set_sensitive(h, TRUE);
2394         if (repr) {
2395             g_object_set_data( tbl, "repr", repr );
2396             g_object_set_data( tbl, "item", item );
2397             Inkscape::GC::anchor(repr);
2398             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2399             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2400         }
2401     } else {
2402         // FIXME: implement averaging of all parameters for multiple selected
2403         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2404         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2405         sp_rtb_sensitivize( tbl );
2406     }
2410 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2412     EgeAdjustmentAction* eact = 0;
2413     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2415     {
2416         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2417         ege_output_action_set_use_markup( act, TRUE );
2418         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2419         g_object_set_data( holder, "mode_action", act );
2420     }
2422     // rx/ry units menu: create
2423     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2424     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2425     // fixme: add % meaning per cent of the width/height
2426     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2427     g_object_set_data( holder, "tracker", tracker );
2429     /* W */
2430     {
2431         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2432         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2433         eact = create_adjustment_action( "RectWidthAction",
2434                                          _("Width"), _("W:"), _("Width of rectangle"),
2435                                          "tools.shapes.rect", "width", 0,
2436                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2437                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2438                                          labels, values, G_N_ELEMENTS(labels),
2439                                          sp_rtb_width_value_changed );
2440         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2441         g_object_set_data( holder, "width_action", eact );
2442         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2443         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2444     }
2446     /* H */
2447     {
2448         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2449         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2450         eact = create_adjustment_action( "RectHeightAction",
2451                                          _("Height"), _("H:"), _("Height of rectangle"),
2452                                          "tools.shapes.rect", "height", 0,
2453                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2454                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2455                                          labels, values, G_N_ELEMENTS(labels),
2456                                          sp_rtb_height_value_changed );
2457         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2458         g_object_set_data( holder, "height_action", eact );
2459         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2460         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2461     }
2463     /* rx */
2464     {
2465         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2466         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2467         eact = create_adjustment_action( "RadiusXAction",
2468                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2469                                          "tools.shapes.rect", "rx", 0,
2470                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2471                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2472                                          labels, values, G_N_ELEMENTS(labels),
2473                                          sp_rtb_rx_value_changed);
2474         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2475         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2476     }
2478     /* ry */
2479     {
2480         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2481         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2482         eact = create_adjustment_action( "RadiusYAction",
2483                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2484                                          "tools.shapes.rect", "ry", 0,
2485                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2486                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2487                                          labels, values, G_N_ELEMENTS(labels),
2488                                          sp_rtb_ry_value_changed);
2489         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2490         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2491     }
2493     // add the units menu
2494     {
2495         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2496         gtk_action_group_add_action( mainActions, act );
2497     }
2499     /* Reset */
2500     {
2501         InkAction* inky = ink_action_new( "RectResetAction",
2502                                           _("Not rounded"),
2503                                           _("Make corners sharp"),
2504                                           "squared_corner",
2505                                           secondarySize );
2506         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2507         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2508         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2509         g_object_set_data( holder, "not_rounded", inky );
2510     }
2512     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2513     sp_rtb_sensitivize( holder );
2515     sigc::connection *connection = new sigc::connection(
2516         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2517         );
2518     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2519     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2522 //########################
2523 //##       3D Box       ##
2524 //########################
2526 // normalize angle so that it lies in the interval [0,360]
2527 static double box3d_normalize_angle (double a) {
2528     double angle = a + ((int) (a/360.0))*360;
2529     if (angle < 0) {
2530         angle += 360.0;
2531     }
2532     return angle;
2535 static void
2536 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2537                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2538     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2539     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2540     //       are reset).
2541     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2543     if (is_infinite) {
2544         gtk_toggle_action_set_active(tact, TRUE);
2545         gtk_action_set_sensitive(act, TRUE);
2547         double angle = persp3d_get_infinite_angle(persp, axis);
2548         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2549             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2550         }
2551     } else {
2552         gtk_toggle_action_set_active(tact, FALSE);
2553         gtk_action_set_sensitive(act, FALSE);
2554     }
2557 static void
2558 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2559     if (!persp_repr) {
2560         g_print ("No perspective given to box3d_resync_toolbar().\n");
2561         return;
2562     }
2564     GtkWidget *tbl = GTK_WIDGET(data);
2565     GtkAdjustment *adj = 0;
2566     GtkAction *act = 0;
2567     GtkToggleAction *tact = 0;
2568     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2569     {
2570         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2571         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2572         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2574         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2575     }
2576     {
2577         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2578         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2579         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2581         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2582     }
2583     {
2584         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2585         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2586         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2588         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2589     }
2592 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2593                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2594                                                   bool /*is_interactive*/, gpointer data)
2596     GtkWidget *tbl = GTK_WIDGET(data);
2598     // quit if run by the attr_changed listener
2599     // note: it used to work without the differently called freeze_ attributes (here and in
2600     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2601     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2602         return;
2603     }
2605     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2606     // sp_document_maybe_done() when the document is undo insensitive)
2607     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2609     // TODO: Only update the appropriate part of the toolbar
2610 //    if (!strcmp(name, "inkscape:vp_z")) {
2611         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2612 //    }
2614     Persp3D *persp = persp3d_get_from_repr(repr);
2615     persp3d_update_box_reprs(persp);
2617     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2620 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2622     NULL, /* child_added */
2623     NULL, /* child_removed */
2624     box3d_persp_tb_event_attr_changed,
2625     NULL, /* content_changed */
2626     NULL  /* order_changed */
2627 };
2629 /**
2630  *  \param selection Should not be NULL.
2631  */
2632 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2633 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2634 static void
2635 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2637     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2638     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2639     // update the perspectives with infinite VPs and leave the other ones untouched).
2641     Inkscape::XML::Node *persp_repr = NULL;
2642     purge_repr_listener(tbl, tbl);
2644     SPItem *item = selection->singleItem();
2645     if (item && SP_IS_BOX3D(item)) {
2646         // FIXME: Also deal with multiple selected boxes
2647         SPBox3D *box = SP_BOX3D(item);
2648         Persp3D *persp = box3d_get_perspective(box);
2649         persp_repr = SP_OBJECT_REPR(persp);
2650         if (persp_repr) {
2651             g_object_set_data(tbl, "repr", persp_repr);
2652             Inkscape::GC::anchor(persp_repr);
2653             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2654             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2655         }
2657         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2658         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2660         box3d_resync_toolbar(persp_repr, tbl);
2661     }
2664 static void
2665 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2667     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2668     SPDocument *document = sp_desktop_document(desktop);
2670     // quit if run by the attr_changed listener
2671     // note: it used to work without the differently called freeze_ attributes (here and in
2672     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2673     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2674         return;
2675     }
2677     // in turn, prevent listener from responding
2678     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2680     //Persp3D *persp = document->current_persp3d;
2681     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2682     if (sel_persps.empty()) {
2683         // this can happen when the document is created; we silently ignore it
2684         return;
2685     }
2686     Persp3D *persp = sel_persps.front();
2688     persp->tmat.set_infinite_direction (axis, adj->value);
2689     SP_OBJECT(persp)->updateRepr();
2691     // TODO: use the correct axis here, too
2692     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2694     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2698 static void
2699 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2701     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2704 static void
2705 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2707     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2710 static void
2711 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2713     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2717 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2719     // TODO: Take all selected perspectives into account
2720     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2721     if (sel_persps.empty()) {
2722         // this can happen when the document is created; we silently ignore it
2723         return;
2724     }
2725     Persp3D *persp = sel_persps.front();
2727     bool set_infinite = gtk_toggle_action_get_active(act);
2728     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2731 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2733     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2736 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2738     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2741 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2743     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2746 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2748     EgeAdjustmentAction* eact = 0;
2749     SPDocument *document = sp_desktop_document (desktop);
2750     Persp3D *persp = document->current_persp3d;
2752     EgeAdjustmentAction* box3d_angle_x = 0;
2753     EgeAdjustmentAction* box3d_angle_y = 0;
2754     EgeAdjustmentAction* box3d_angle_z = 0;
2756     /* Angle X */
2757     {
2758         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2759         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2760         eact = create_adjustment_action( "3DBoxAngleXAction",
2761                                          _("Angle in X direction"), _("Angle X:"),
2762                                          // Translators: PL is short for 'perspective line'
2763                                          _("Angle of PLs in X direction"),
2764                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2765                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2766                                          -360.0, 360.0, 1.0, 10.0,
2767                                          labels, values, G_N_ELEMENTS(labels),
2768                                          box3d_angle_x_value_changed );
2769         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2770         g_object_set_data( holder, "box3d_angle_x_action", eact );
2771         box3d_angle_x = eact;
2772     }
2774     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2775         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2776     } else {
2777         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2778     }
2781     /* VP X state */
2782     {
2783         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2784                                                       // Translators: VP is short for 'vanishing point'
2785                                                       _("State of VP in X direction"),
2786                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2787                                                       "toggle_vp_x",
2788                                                       Inkscape::ICON_SIZE_DECORATION );
2789         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2790         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2791         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2792         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2793         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2794     }
2796     /* Angle Y */
2797     {
2798         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2799         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2800         eact = create_adjustment_action( "3DBoxAngleYAction",
2801                                          _("Angle in Y direction"), _("Angle Y:"),
2802                                          // Translators: PL is short for 'perspective line'
2803                                          _("Angle of PLs in Y direction"),
2804                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2805                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2806                                          -360.0, 360.0, 1.0, 10.0,
2807                                          labels, values, G_N_ELEMENTS(labels),
2808                                          box3d_angle_y_value_changed );
2809         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2810         g_object_set_data( holder, "box3d_angle_y_action", eact );
2811         box3d_angle_y = eact;
2812     }
2814     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2815         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2816     } else {
2817         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2818     }
2820     /* VP Y state */
2821     {
2822         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2823                                                       // Translators: VP is short for 'vanishing point'
2824                                                       _("State of VP in Y direction"),
2825                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2826                                                       "toggle_vp_y",
2827                                                       Inkscape::ICON_SIZE_DECORATION );
2828         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2829         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2830         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2831         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2832         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2833     }
2835     /* Angle Z */
2836     {
2837         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2838         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2839         eact = create_adjustment_action( "3DBoxAngleZAction",
2840                                          _("Angle in Z direction"), _("Angle Z:"),
2841                                          // Translators: PL is short for 'perspective line'
2842                                          _("Angle of PLs in Z direction"),
2843                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2844                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2845                                          -360.0, 360.0, 1.0, 10.0,
2846                                          labels, values, G_N_ELEMENTS(labels),
2847                                          box3d_angle_z_value_changed );
2848         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2849         g_object_set_data( holder, "box3d_angle_z_action", eact );
2850         box3d_angle_z = eact;
2851     }
2853     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2854         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2855     } else {
2856         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2857     }
2859     /* VP Z state */
2860     {
2861         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2862                                                       // Translators: VP is short for 'vanishing point'
2863                                                       _("State of VP in Z direction"),
2864                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2865                                                       "toggle_vp_z",
2866                                                       Inkscape::ICON_SIZE_DECORATION );
2867         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2868         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2869         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2870         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2871         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2872     }
2874     sigc::connection *connection = new sigc::connection(
2875         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2876        );
2877     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2878     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2881 //########################
2882 //##       Spiral       ##
2883 //########################
2885 static void
2886 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2888     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2890     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2891         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2892     }
2894     // quit if run by the attr_changed listener
2895     if (g_object_get_data( tbl, "freeze" )) {
2896         return;
2897     }
2899     // in turn, prevent listener from responding
2900     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2902     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2904     bool modmade = false;
2905     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2906          items != NULL;
2907          items = items->next)
2908     {
2909         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2910             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2911             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2912             SP_OBJECT((SPItem *) items->data)->updateRepr();
2913             modmade = true;
2914         }
2915     }
2917     g_free(namespaced_name);
2919     if (modmade) {
2920         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2921                                    _("Change spiral"));
2922     }
2924     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2927 static void
2928 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2930     sp_spl_tb_value_changed(adj, tbl, "revolution");
2933 static void
2934 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2936     sp_spl_tb_value_changed(adj, tbl, "expansion");
2939 static void
2940 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2942     sp_spl_tb_value_changed(adj, tbl, "t0");
2945 static void
2946 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2948     GtkWidget *tbl = GTK_WIDGET(obj);
2950     GtkAdjustment *adj;
2952     // fixme: make settable
2953     gdouble rev = 5;
2954     gdouble exp = 1.0;
2955     gdouble t0 = 0.0;
2957     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2958     gtk_adjustment_set_value(adj, rev);
2959     gtk_adjustment_value_changed(adj);
2961     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2962     gtk_adjustment_set_value(adj, exp);
2963     gtk_adjustment_value_changed(adj);
2965     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2966     gtk_adjustment_set_value(adj, t0);
2967     gtk_adjustment_value_changed(adj);
2969     spinbutton_defocus(GTK_OBJECT(tbl));
2973 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2974                                          gchar const */*old_value*/, gchar const */*new_value*/,
2975                                          bool /*is_interactive*/, gpointer data)
2977     GtkWidget *tbl = GTK_WIDGET(data);
2979     // quit if run by the _changed callbacks
2980     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2981         return;
2982     }
2984     // in turn, prevent callbacks from responding
2985     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2987     GtkAdjustment *adj;
2988     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2989     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2991     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2992     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2994     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2995     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2997     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3001 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3002     NULL, /* child_added */
3003     NULL, /* child_removed */
3004     spiral_tb_event_attr_changed,
3005     NULL, /* content_changed */
3006     NULL  /* order_changed */
3007 };
3009 static void
3010 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3012     int n_selected = 0;
3013     Inkscape::XML::Node *repr = NULL;
3015     purge_repr_listener( tbl, tbl );
3017     for (GSList const *items = selection->itemList();
3018          items != NULL;
3019          items = items->next)
3020     {
3021         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3022             n_selected++;
3023             repr = SP_OBJECT_REPR((SPItem *) items->data);
3024         }
3025     }
3027     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3029     if (n_selected == 0) {
3030         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3031     } else if (n_selected == 1) {
3032         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3034         if (repr) {
3035             g_object_set_data( tbl, "repr", repr );
3036             Inkscape::GC::anchor(repr);
3037             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3038             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3039         }
3040     } else {
3041         // FIXME: implement averaging of all parameters for multiple selected
3042         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3043         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3044     }
3048 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3050     EgeAdjustmentAction* eact = 0;
3051     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3053     {
3054         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3055         ege_output_action_set_use_markup( act, TRUE );
3056         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3057         g_object_set_data( holder, "mode_action", act );
3058     }
3060     /* Revolution */
3061     {
3062         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3063         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3064         eact = create_adjustment_action( "SpiralRevolutionAction",
3065                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3066                                          "tools.shapes.spiral", "revolution", 3.0,
3067                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3068                                          0.01, 1024.0, 0.1, 1.0,
3069                                          labels, values, G_N_ELEMENTS(labels),
3070                                          sp_spl_tb_revolution_value_changed, 1, 2);
3071         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3072     }
3074     /* Expansion */
3075     {
3076         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3077         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3078         eact = create_adjustment_action( "SpiralExpansionAction",
3079                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3080                                          "tools.shapes.spiral", "expansion", 1.0,
3081                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3082                                          0.0, 1000.0, 0.01, 1.0,
3083                                          labels, values, G_N_ELEMENTS(labels),
3084                                          sp_spl_tb_expansion_value_changed);
3085         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3086     }
3088     /* T0 */
3089     {
3090         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3091         gdouble values[] = {0, 0.5, 0.9};
3092         eact = create_adjustment_action( "SpiralT0Action",
3093                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3094                                          "tools.shapes.spiral", "t0", 0.0,
3095                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3096                                          0.0, 0.999, 0.01, 1.0,
3097                                          labels, values, G_N_ELEMENTS(labels),
3098                                          sp_spl_tb_t0_value_changed);
3099         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3100     }
3102     /* Reset */
3103     {
3104         InkAction* inky = ink_action_new( "SpiralResetAction",
3105                                           _("Defaults"),
3106                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3107                                           GTK_STOCK_CLEAR,
3108                                           secondarySize );
3109         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3110         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3111     }
3114     sigc::connection *connection = new sigc::connection(
3115         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3116         );
3117     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3118     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3121 //########################
3122 //##     Pen/Pencil    ##
3123 //########################
3125 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3127     prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3130 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3132     // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3133     //        adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3134     {
3135         EgeOutputAction* act = ege_output_action_new(
3136             tool_is_pencil ?
3137             "FreehandModeActionPencilTemp" :
3138             "FreehandModeActionPenTemp",
3139             _("<b>Mode:</b>"), "", 0 );
3140         ege_output_action_set_use_markup( act, TRUE );
3141         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3142         g_object_set_data( holder, "freehand_mode_action", act );
3143     }
3145     /* Freehand mode toggle buttons */
3146     {
3147         //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3148         //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3149         guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3150         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3152         {
3153             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3155             GtkTreeIter iter;
3156             gtk_list_store_append( model, &iter );
3157             gtk_list_store_set( model, &iter,
3158                                 0, _("Bézier"),
3159                                 1, _("Regular Bézier mode"),
3160                                 2, "bezier_mode",
3161                                 -1 );
3163             gtk_list_store_append( model, &iter );
3164             gtk_list_store_set( model, &iter,
3165                                 0, _("Spiro"),
3166                                 1, _("Spiro splines mode"),
3167                                 2, "spiro_splines_mode",
3168                                 -1 );
3170             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3171                                                                 "FreehandModeActionPencil" :
3172                                                                 "FreehandModeActionPen",
3173                                                                 (""), (""), NULL, GTK_TREE_MODEL(model) );
3174             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3175             g_object_set_data( holder, "freehande_mode_action", act );
3177             ege_select_one_action_set_appearance( act, "full" );
3178             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3179             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3180             ege_select_one_action_set_icon_column( act, 2 );
3181             ege_select_one_action_set_icon_size( act, secondarySize );
3182             ege_select_one_action_set_tooltip_column( act, 1  );
3184             ege_select_one_action_set_active( act, spiroMode);
3185             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3186         }
3187     }
3190 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3192     sp_add_spiro_toggle(mainActions, holder, false);
3196 static void
3197 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3199     GtkWidget *tbl = GTK_WIDGET(obj);
3201     GtkAdjustment *adj;
3203     // fixme: make settable
3204     gdouble tolerance = 4;
3206     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3207     gtk_adjustment_set_value(adj, tolerance);
3208     gtk_adjustment_value_changed(adj);
3210     spinbutton_defocus(GTK_OBJECT(tbl));
3213 static void
3214 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3217     // quit if run by the attr_changed listener
3218     if (g_object_get_data( tbl, "freeze" )) {
3219         return;
3220     }
3221     // in turn, prevent listener from responding
3222     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3223     prefs_set_double_attribute("tools.freehand.pencil", 
3224                                "tolerance", adj->value);
3225     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3226     
3231 static void
3232 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node *repr, 
3233                                               const gchar *key, 
3234                                               const gchar *oldval, 
3235                                               const gchar *newval, 
3236                                               bool is_interactive, 
3237                                               void * data)
3239     GObject* tbl = G_OBJECT(data);
3240     if (g_object_get_data( tbl, "freeze" )) {
3241         return;
3242     }    
3244     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3246     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3247                                                             "tolerance");
3248     
3249     double v = prefs_get_double_attribute("tools.freehand.pencil", 
3250                                             "tolerance", adj->value);
3251     gtk_adjustment_set_value(adj, v);
3252     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3253     
3256 static Inkscape::XML::NodeEventVector pencil_node_events = 
3258     NULL, 
3259     NULL, 
3260     sp_pencil_tb_tolerance_value_changed_external,
3261     NULL, 
3262     NULL,
3263 };
3266 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3268     sp_add_spiro_toggle(mainActions, holder, true);
3270     EgeAdjustmentAction* eact = 0;
3272     /* Tolerance */
3273     {
3275         eact = create_adjustment_action( "PencilToleranceAction",
3276                  _("Number of pixels allowed in interpolating"), 
3277                                          _("Tolerance:"), _("Tolerance"),
3278                                          "tools.freehand.pencil", "tolerance", 
3279                                          3.0,
3280                                          GTK_WIDGET(desktop->canvas), NULL, 
3281                                          holder, TRUE, "altx-pencil",
3282                                          0.5, 100.0, 0.5, 1.0, 
3283                                          NULL, NULL, 0,
3284                                          sp_pencil_tb_tolerance_value_changed,
3285                                          1, 2);
3286         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3288         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, 
3289                                                       "tools.freehand.pencil");
3290         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3291         g_object_set_data(G_OBJECT(holder), "repr", repr);
3293     }
3294     /* Reset */
3295     {
3296         InkAction* inky = ink_action_new( "PencilResetAction",
3297                                           _("Defaults"),
3298                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3299                                           GTK_STOCK_CLEAR,
3300                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3301         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3302         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3303     }
3304     
3305     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3310 //########################
3311 //##       Tweak        ##
3312 //########################
3314 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3316     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3319 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3321     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3324 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3326     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3329 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3331     int mode = ege_select_one_action_get_active( act );
3332     prefs_set_int_attribute("tools.tweak", "mode", mode);
3334     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3335     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3336     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3337     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3338     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3339     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3340     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3341         if (doh) gtk_action_set_sensitive (doh, TRUE);
3342         if (dos) gtk_action_set_sensitive (dos, TRUE);
3343         if (dol) gtk_action_set_sensitive (dol, TRUE);
3344         if (doo) gtk_action_set_sensitive (doo, TRUE);
3345         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3346         if (fid) gtk_action_set_sensitive (fid, FALSE);
3347     } else {
3348         if (doh) gtk_action_set_sensitive (doh, FALSE);
3349         if (dos) gtk_action_set_sensitive (dos, FALSE);
3350         if (dol) gtk_action_set_sensitive (dol, FALSE);
3351         if (doo) gtk_action_set_sensitive (doo, FALSE);
3352         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3353         if (fid) gtk_action_set_sensitive (fid, TRUE);
3354     }
3357 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3359     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3362 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3363     bool show = gtk_toggle_action_get_active( act );
3364     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3366 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3367     bool show = gtk_toggle_action_get_active( act );
3368     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3370 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3371     bool show = gtk_toggle_action_get_active( act );
3372     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3374 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3375     bool show = gtk_toggle_action_get_active( act );
3376     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3379 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3381     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3383     {
3384         /* Width */
3385         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3386         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3387         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3388                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3389                                                               "tools.tweak", "width", 15,
3390                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3391                                                               1, 100, 1.0, 10.0,
3392                                                               labels, values, G_N_ELEMENTS(labels),
3393                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3394         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3395         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3396     }
3399     {
3400         /* Force */
3401         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3402         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3403         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3404                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3405                                                               "tools.tweak", "force", 20,
3406                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3407                                                               1, 100, 1.0, 10.0,
3408                                                               labels, values, G_N_ELEMENTS(labels),
3409                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3410         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3411         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3412     }
3414     /* Mode */
3415     {
3416         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3418         GtkTreeIter iter;
3419         gtk_list_store_append( model, &iter );
3420         gtk_list_store_set( model, &iter,
3421                             0, _("Push mode"),
3422                             1, _("Push parts of paths in any direction"),
3423                             2, "tweak_push_mode",
3424                             -1 );
3426         gtk_list_store_append( model, &iter );
3427         gtk_list_store_set( model, &iter,
3428                             0, _("Shrink mode"),
3429                             1, _("Shrink (inset) parts of paths"),
3430                             2, "tweak_shrink_mode",
3431                             -1 );
3433         gtk_list_store_append( model, &iter );
3434         gtk_list_store_set( model, &iter,
3435                             0, _("Grow mode"),
3436                             1, _("Grow (outset) parts of paths"),
3437                             2, "tweak_grow_mode",
3438                             -1 );
3440         gtk_list_store_append( model, &iter );
3441         gtk_list_store_set( model, &iter,
3442                             0, _("Attract mode"),
3443                             1, _("Attract parts of paths towards cursor"),
3444                             2, "tweak_attract_mode",
3445                             -1 );
3447         gtk_list_store_append( model, &iter );
3448         gtk_list_store_set( model, &iter,
3449                             0, _("Repel mode"),
3450                             1, _("Repel parts of paths from cursor"),
3451                             2, "tweak_repel_mode",
3452                             -1 );
3454         gtk_list_store_append( model, &iter );
3455         gtk_list_store_set( model, &iter,
3456                             0, _("Roughen mode"),
3457                             1, _("Roughen parts of paths"),
3458                             2, "tweak_roughen_mode",
3459                             -1 );
3461         gtk_list_store_append( model, &iter );
3462         gtk_list_store_set( model, &iter,
3463                             0, _("Color paint mode"),
3464                             1, _("Paint the tool's color upon selected objects"),
3465                             2, "tweak_colorpaint_mode",
3466                             -1 );
3468         gtk_list_store_append( model, &iter );
3469         gtk_list_store_set( model, &iter,
3470                             0, _("Color jitter mode"),
3471                             1, _("Jitter the colors of selected objects"),
3472                             2, "tweak_colorjitter_mode",
3473                             -1 );
3475         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3476         g_object_set( act, "short_label", _("Mode:"), NULL );
3477         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3478         g_object_set_data( holder, "mode_action", act );
3480         ege_select_one_action_set_appearance( act, "full" );
3481         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3482         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3483         ege_select_one_action_set_icon_column( act, 2 );
3484         ege_select_one_action_set_icon_size( act, secondarySize );
3485         ege_select_one_action_set_tooltip_column( act, 1  );
3487         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3488         ege_select_one_action_set_active( act, mode );
3489         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3491         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3492     }
3494     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3496     {
3497         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3498         ege_output_action_set_use_markup( act, TRUE );
3499         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3500         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3501             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3502         g_object_set_data( holder, "tweak_channels_label", act);
3503     }
3505     {
3506         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3507                                                       _("Hue"),
3508                                                       _("In color mode, act on objects' hue"),
3509                                                       NULL,
3510                                                       Inkscape::ICON_SIZE_DECORATION );
3511         //TRANSLATORS:  "H" here stands for hue
3512         g_object_set( act, "short_label", _("H"), NULL );
3513         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3514         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3515         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3516         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3517             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3518         g_object_set_data( holder, "tweak_doh", act);
3519     }
3520     {
3521         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3522                                                       _("Saturation"),
3523                                                       _("In color mode, act on objects' saturation"),
3524                                                       NULL,
3525                                                       Inkscape::ICON_SIZE_DECORATION );
3526         //TRANSLATORS: "S" here stands for Saturation
3527         g_object_set( act, "short_label", _("S"), NULL );
3528         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3529         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3530         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3531         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3532             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3533         g_object_set_data( holder, "tweak_dos", act );
3534     }
3535     {
3536         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3537                                                       _("Lightness"),
3538                                                       _("In color mode, act on objects' lightness"),
3539                                                       NULL,
3540                                                       Inkscape::ICON_SIZE_DECORATION );
3541         //TRANSLATORS: "L" here stands for Lightness
3542         g_object_set( act, "short_label", _("L"), NULL );
3543         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3544         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3545         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3546         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3547             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3548         g_object_set_data( holder, "tweak_dol", act );
3549     }
3550     {
3551         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3552                                                       _("Opacity"),
3553                                                       _("In color mode, act on objects' opacity"),
3554                                                       NULL,
3555                                                       Inkscape::ICON_SIZE_DECORATION );
3556         //TRANSLATORS: "O" here stands for Opacity
3557         g_object_set( act, "short_label", _("O"), NULL );
3558         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3559         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3560         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3561         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3562             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3563         g_object_set_data( holder, "tweak_doo", act );
3564     }
3566     {   /* Fidelity */
3567         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3568         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3569         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3570                                                               _("Fidelity"), _("Fidelity:"),
3571                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3572                                                               "tools.tweak", "fidelity", 50,
3573                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3574                                                               1, 100, 1.0, 10.0,
3575                                                               labels, values, G_N_ELEMENTS(labels),
3576                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3577         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3578         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3579         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3580             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3581         g_object_set_data( holder, "tweak_fidelity", eact );
3582     }
3585     /* Use Pressure button */
3586     {
3587         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3588                                                       _("Pressure"),
3589                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3590                                                       "use_pressure",
3591                                                       Inkscape::ICON_SIZE_DECORATION );
3592         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3593         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3594         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3595     }
3600 //########################
3601 //##     Calligraphy    ##
3602 //########################
3603 static void update_presets_list(GObject *dataKludge ){
3604     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3605     if (sel) {
3606         ege_select_one_action_set_active(sel, 0 );
3607     }
3610 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3612     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3613     update_presets_list(tbl);
3616 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3618     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3619     update_presets_list(tbl);
3622 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3624     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3625     update_presets_list(tbl);
3628 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3630     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3631     update_presets_list(tbl);
3634 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3636     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3637     update_presets_list(tbl);
3640 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3642     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3643     update_presets_list(tbl);
3646 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3648     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3649     update_presets_list(tbl);
3652 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3654     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3655     update_presets_list(tbl);
3658 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3660     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3661     update_presets_list(tbl);
3664 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3666     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3667     update_presets_list(tbl);
3670 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3672     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3673     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3674     update_presets_list(tbl);
3675     if (calligraphy_angle )
3676         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3680 #define PROFILE_FLOAT_SIZE 7
3681 #define PROFILE_INT_SIZE 4
3682 struct ProfileFloatElement {
3683     char const *name;
3684     double def;
3685     double min;
3686     double max;
3687 };
3688 struct ProfileIntElement {
3689     char const *name;
3690     int def;
3691     int min;
3692     int max;
3693 };
3697 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3698     {"mass",0.02, 0.0, 1.0},
3699     {"wiggle",0.0, 0.0, 1.0},
3700     {"angle",30.0, -90.0, 90.0},
3701     {"thinning",0.1, -1.0, 1.0},
3702     {"tremor",0.0, 0.0, 1.0},
3703     {"flatness",0.9, 0.0, 1.0},
3704     {"cap_rounding",0.0, 0.0, 5.0}
3705 };
3706 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3707     {"width",15, 1, 100},
3708     {"usepressure",1,0,1},
3709     {"tracebackground",0,0,1},
3710     {"usetilt",1,0,1},
3711 };
3715 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3716     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3717     if (! desktop) return;
3719     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3720     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3721     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3723     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3724     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3725     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3726     
3727     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3728         ProfileFloatElement const &pe = f_profile[i];
3729         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3730         prefs_set_double_attribute(pref_path,pe.name,v);
3731     }    
3732     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3733         ProfileIntElement const &pe = i_profile[i];
3734         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3735         prefs_set_int_attribute(pref_path,pe.name,v);
3736     }
3737     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3739     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3740     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3741     GtkTreeIter iter;
3742     gtk_list_store_append( model, &iter );
3743     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3745     free(profile_id);
3746     free(pref_path);
3748     ege_select_one_action_set_active(selector, new_index);
3752 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3753     
3754     gint preset_index = ege_select_one_action_get_active( act ); 
3755     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3756        
3757     if ( profile_name) {        
3758         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3759         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3760             ProfileFloatElement const &pe = f_profile[i];
3761             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3762             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3763             if ( adj ) {
3764                 gtk_adjustment_set_value(adj, v);
3765             }
3766         } 
3767         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3768             ProfileIntElement const &pe = i_profile[i];
3769             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3770             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3771             if ( toggle ) {
3772                 gtk_toggle_action_set_active(toggle, v);
3773             } else printf("No toggle");
3774         }
3775         free(profile_name);
3776         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3777     }
3782 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3784     {
3785         EgeAdjustmentAction* calligraphy_angle = 0;
3787         {
3788         /* Width */
3789         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3790         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3791         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3792                                                               _("Pen Width"), _("Width:"),
3793                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3794                                                               "tools.calligraphic", "width", 15,
3795                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3796                                                               1, 100, 1.0, 10.0,
3797                                                               labels, values, G_N_ELEMENTS(labels),
3798                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3799         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3800         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3801         }
3803         {
3804         /* Thinning */
3805             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3806             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3807         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3808                                                               _("Stroke Thinning"), _("Thinning:"),
3809                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3810                                                               "tools.calligraphic", "thinning", 0.1,
3811                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3812                                                               -1.0, 1.0, 0.01, 0.1,
3813                                                               labels, values, G_N_ELEMENTS(labels),
3814                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3815         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3816         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3817         }
3819         {
3820         /* Angle */
3821         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3822         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3823         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3824                                                               _("Pen Angle"), _("Angle:"),
3825                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3826                                                               "tools.calligraphic", "angle", 30,
3827                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3828                                                               -90.0, 90.0, 1.0, 10.0,
3829                                                               labels, values, G_N_ELEMENTS(labels),
3830                                                               sp_ddc_angle_value_changed, 1, 0 );
3831         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3832         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3833         g_object_set_data( holder, "angle", eact );
3834         calligraphy_angle = eact;
3835         }
3837         {
3838         /* Fixation */
3839             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3840         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3841         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3842                                                               _("Fixation"), _("Fixation:"),
3843                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3844                                                               "tools.calligraphic", "flatness", 0.9,
3845                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3846                                                               0.0, 1.0, 0.01, 0.1,
3847                                                               labels, values, G_N_ELEMENTS(labels),
3848                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3849         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3850         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3851         }
3853         {
3854         /* Cap Rounding */
3855             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3856         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3857         // TRANSLATORS: "cap" means "end" (both start and finish) here
3858         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3859                                                               _("Cap rounding"), _("Caps:"),
3860                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3861                                                               "tools.calligraphic", "cap_rounding", 0.0,
3862                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3863                                                               0.0, 5.0, 0.01, 0.1,
3864                                                               labels, values, G_N_ELEMENTS(labels),
3865                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3866         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3867         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3868         }
3870         {
3871         /* Tremor */
3872             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3873         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3874         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3875                                                               _("Stroke Tremor"), _("Tremor:"),
3876                                                               _("Increase to make strokes rugged and trembling"),
3877                                                               "tools.calligraphic", "tremor", 0.0,
3878                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3879                                                               0.0, 1.0, 0.01, 0.1,
3880                                                               labels, values, G_N_ELEMENTS(labels),
3881                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3883         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3884         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3885         }
3887         {
3888         /* Wiggle */
3889         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3890         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3891         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3892                                                               _("Pen Wiggle"), _("Wiggle:"),
3893                                                               _("Increase to make the pen waver and wiggle"),
3894                                                               "tools.calligraphic", "wiggle", 0.0,
3895                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3896                                                               0.0, 1.0, 0.01, 0.1,
3897                                                               labels, values, G_N_ELEMENTS(labels),
3898                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3899         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3900         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3901         }
3903         {
3904         /* Mass */
3905             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3906         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3907         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3908                                                               _("Pen Mass"), _("Mass:"),
3909                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3910                                                               "tools.calligraphic", "mass", 0.02,
3911                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3912                                                               0.0, 1.0, 0.01, 0.1,
3913                                                               labels, values, G_N_ELEMENTS(labels),
3914                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3915         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3916         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3917         }
3920         /* Trace Background button */
3921         {
3922             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3923                                                           _("Trace Background"),
3924                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3925                                                           "trace_background",
3926                                                           Inkscape::ICON_SIZE_DECORATION );
3927             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3928             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3929             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3930             g_object_set_data( holder, "tracebackground", act );
3931         }
3933         /* Use Pressure button */
3934         {
3935             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3936                                                           _("Pressure"),
3937                                                           _("Use the pressure of the input device to alter the width of the pen"),
3938                                                           "use_pressure",
3939                                                           Inkscape::ICON_SIZE_DECORATION );
3940             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3941             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3942             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3943             g_object_set_data( holder, "usepressure", act );
3944         }
3946         /* Use Tilt button */
3947         {
3948             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3949                                                           _("Tilt"),
3950                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3951                                                           "use_tilt",
3952                                                           Inkscape::ICON_SIZE_DECORATION );
3953             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3954             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3955             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3956             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3957             g_object_set_data( holder, "usetilt", act );
3958         }
3960         /*calligraphic profile */
3961         {
3962             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3963             gchar *pref_path;
3966             GtkTreeIter iter;
3967             gtk_list_store_append( model, &iter );
3968             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3970             //TODO: switch back to prefs API
3971             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3972             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3973             int ii=1;
3974             while (child_repr) {
3975                 GtkTreeIter iter;
3976                 char *preset_name = (char *) child_repr->attribute("name");
3977                 gtk_list_store_append( model, &iter );                
3978                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
3979                 child_repr = sp_repr_next(child_repr); 
3980             }
3982             pref_path = NULL;
3983             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
3984             ege_select_one_action_set_appearance( act1, "compact" );
3985             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3986             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3987             g_object_set_data( holder, "profile_selector", act1 );
3988         
3989         }
3991         /*Save or delete calligraphic profile */
3992         {
3993             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3994                                              _("Defaults"),
3995                                              _("Save current settings as new profile"),
3996                                              GTK_STOCK_SAVE );
3997             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4000             gtk_action_group_add_action( mainActions, act );
4001             gtk_action_set_sensitive( act, TRUE );
4002             g_object_set_data( holder, "profile_save_delete", act );
4003         }
4004     }
4008 //########################
4009 //##    Circle / Arc    ##
4010 //########################
4012 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4014     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4015     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4017     if (v1 == 0 && v2 == 0) {
4018         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4019             gtk_action_set_sensitive( ocb, FALSE );
4020             gtk_action_set_sensitive( make_whole, FALSE );
4021         }
4022     } else {
4023         gtk_action_set_sensitive( ocb, TRUE );
4024         gtk_action_set_sensitive( make_whole, TRUE );
4025     }
4028 static void
4029 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4031     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4033     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4034         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4035     }
4037     // quit if run by the attr_changed listener
4038     if (g_object_get_data( tbl, "freeze" )) {
4039         return;
4040     }
4042     // in turn, prevent listener from responding
4043     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4045     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4047     bool modmade = false;
4048     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4049          items != NULL;
4050          items = items->next)
4051     {
4052         SPItem *item = SP_ITEM(items->data);
4054         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4056             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4057             SPArc *arc = SP_ARC(item);
4059             if (!strcmp(value_name, "start"))
4060                 ge->start = (adj->value * M_PI)/ 180;
4061             else
4062                 ge->end = (adj->value * M_PI)/ 180;
4064             sp_genericellipse_normalize(ge);
4065             ((SPObject *)arc)->updateRepr();
4066             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4068             modmade = true;
4069         }
4070     }
4072     g_free(namespaced_name);
4074     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4076     sp_arctb_sensitivize( tbl, adj->value, other->value );
4078     if (modmade) {
4079         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4080                                    _("Arc: Change start/end"));
4081     }
4083     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4087 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4089     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4092 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4094     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4098 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4100     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4101     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4102     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4103         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4104     }
4106     // only take action if run by the attr_changed listener
4107     if (!g_object_get_data( tbl, "freeze" )) {
4108         // in turn, prevent listener from responding
4109         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4111         if ( eraserMode != 0 ) {
4112         } else {
4113         }
4114         // TODO finish implementation
4116         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4117     }
4120 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4122     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4123     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4124         if ( ege_select_one_action_get_active( act ) != 0 ) {
4125             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4126         } else {
4127             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4128         }
4129     }
4131     // quit if run by the attr_changed listener
4132     if (g_object_get_data( tbl, "freeze" )) {
4133         return;
4134     }
4136     // in turn, prevent listener from responding
4137     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4139     bool modmade = false;
4141     if ( ege_select_one_action_get_active(act) != 0 ) {
4142         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4143              items != NULL;
4144              items = items->next)
4145         {
4146             if (SP_IS_ARC((SPItem *) items->data)) {
4147                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4148                 repr->setAttribute("sodipodi:open", "true");
4149                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4150                 modmade = true;
4151             }
4152         }
4153     } else {
4154         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4155              items != NULL;
4156              items = items->next)
4157         {
4158             if (SP_IS_ARC((SPItem *) items->data))    {
4159                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4160                 repr->setAttribute("sodipodi:open", NULL);
4161                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4162                 modmade = true;
4163             }
4164         }
4165     }
4167     if (modmade) {
4168         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4169                                    _("Arc: Change open/closed"));
4170     }
4172     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4175 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4177     GtkAdjustment *adj;
4178     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4179     gtk_adjustment_set_value(adj, 0.0);
4180     gtk_adjustment_value_changed(adj);
4182     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4183     gtk_adjustment_set_value(adj, 0.0);
4184     gtk_adjustment_value_changed(adj);
4186     spinbutton_defocus( GTK_OBJECT(obj) );
4189 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4190                                       gchar const */*old_value*/, gchar const */*new_value*/,
4191                                       bool /*is_interactive*/, gpointer data)
4193     GObject *tbl = G_OBJECT(data);
4195     // quit if run by the _changed callbacks
4196     if (g_object_get_data( tbl, "freeze" )) {
4197         return;
4198     }
4200     // in turn, prevent callbacks from responding
4201     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4203     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4204     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4206     GtkAdjustment *adj1,*adj2;
4207     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4208     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4209     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4210     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4212     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4214     char const *openstr = NULL;
4215     openstr = repr->attribute("sodipodi:open");
4216     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4218     if (openstr) {
4219         ege_select_one_action_set_active( ocb, 1 );
4220     } else {
4221         ege_select_one_action_set_active( ocb, 0 );
4222     }
4224     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4227 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4228     NULL, /* child_added */
4229     NULL, /* child_removed */
4230     arc_tb_event_attr_changed,
4231     NULL, /* content_changed */
4232     NULL  /* order_changed */
4233 };
4236 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4238     int n_selected = 0;
4239     Inkscape::XML::Node *repr = NULL;
4241     purge_repr_listener( tbl, tbl );
4243     for (GSList const *items = selection->itemList();
4244          items != NULL;
4245          items = items->next)
4246     {
4247         if (SP_IS_ARC((SPItem *) items->data)) {
4248             n_selected++;
4249             repr = SP_OBJECT_REPR((SPItem *) items->data);
4250         }
4251     }
4253     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4255     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4256     if (n_selected == 0) {
4257         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4258     } else if (n_selected == 1) {
4259         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4260         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4262         if (repr) {
4263             g_object_set_data( tbl, "repr", repr );
4264             Inkscape::GC::anchor(repr);
4265             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4266             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4267         }
4268     } else {
4269         // FIXME: implement averaging of all parameters for multiple selected
4270         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4271         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4272         sp_arctb_sensitivize( tbl, 1, 0 );
4273     }
4277 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4279     EgeAdjustmentAction* eact = 0;
4280     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4283     {
4284         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4285         ege_output_action_set_use_markup( act, TRUE );
4286         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4287         g_object_set_data( holder, "mode_action", act );
4288     }
4290     /* Start */
4291     {
4292         eact = create_adjustment_action( "ArcStartAction",
4293                                          _("Start"), _("Start:"),
4294                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4295                                          "tools.shapes.arc", "start", 0.0,
4296                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4297                                          -360.0, 360.0, 1.0, 10.0,
4298                                          0, 0, 0,
4299                                          sp_arctb_start_value_changed);
4300         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4301     }
4303     /* End */
4304     {
4305         eact = create_adjustment_action( "ArcEndAction",
4306                                          _("End"), _("End:"),
4307                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4308                                          "tools.shapes.arc", "end", 0.0,
4309                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4310                                          -360.0, 360.0, 1.0, 10.0,
4311                                          0, 0, 0,
4312                                          sp_arctb_end_value_changed);
4313         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4314     }
4316     /* Segments / Pie checkbox */
4317     {
4318         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4320         GtkTreeIter iter;
4321         gtk_list_store_append( model, &iter );
4322         gtk_list_store_set( model, &iter,
4323                             0, _("Closed arc"),
4324                             1, _("Switch to segment (closed shape with two radii)"),
4325                             2, "circle_closed_arc",
4326                             -1 );
4328         gtk_list_store_append( model, &iter );
4329         gtk_list_store_set( model, &iter,
4330                             0, _("Open Arc"),
4331                             1, _("Switch to arc (unclosed shape)"),
4332                             2, "circle_open_arc",
4333                             -1 );
4335         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4336         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4337         g_object_set_data( holder, "open_action", act );
4339         ege_select_one_action_set_appearance( act, "full" );
4340         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4341         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4342         ege_select_one_action_set_icon_column( act, 2 );
4343         ege_select_one_action_set_icon_size( act, secondarySize );
4344         ege_select_one_action_set_tooltip_column( act, 1  );
4346         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4347         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4348         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4349         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4350     }
4352     /* Make Whole */
4353     {
4354         InkAction* inky = ink_action_new( "ArcResetAction",
4355                                           _("Make whole"),
4356                                           _("Make the shape a whole ellipse, not arc or segment"),
4357                                           "reset_circle",
4358                                           secondarySize );
4359         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4360         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4361         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4362         g_object_set_data( holder, "make_whole", inky );
4363     }
4365     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4366     // sensitivize make whole and open checkbox
4367     {
4368         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4369         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4370         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4371     }
4374     sigc::connection *connection = new sigc::connection(
4375         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4376         );
4377     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4378     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4384 // toggle button callbacks and updaters
4386 //########################
4387 //##      Dropper       ##
4388 //########################
4390 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4391     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4392     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4393     if ( set_action ) {
4394         if ( gtk_toggle_action_get_active( act ) ) {
4395             gtk_action_set_sensitive( set_action, TRUE );
4396         } else {
4397             gtk_action_set_sensitive( set_action, FALSE );
4398         }
4399     }
4401     spinbutton_defocus(GTK_OBJECT(tbl));
4404 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4405     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4406     spinbutton_defocus(GTK_OBJECT(tbl));
4410 /**
4411  * Dropper auxiliary toolbar construction and setup.
4412  *
4413  * TODO: Would like to add swatch of current color.
4414  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4415  *       can drag and drop places. Will provide a nice mixing palette.
4416  */
4417 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4419     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4421     {
4422         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4423         ege_output_action_set_use_markup( act, TRUE );
4424         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4425     }
4427     {
4428         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4429                                                       _("Pick opacity"),
4430                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4431                                                       NULL,
4432                                                       Inkscape::ICON_SIZE_DECORATION );
4433         g_object_set( act, "short_label", _("Pick"), NULL );
4434         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4435         g_object_set_data( holder, "pick_action", act );
4436         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4437         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4438     }
4440     {
4441         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4442                                                       _("Assign opacity"),
4443                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4444                                                       NULL,
4445                                                       Inkscape::ICON_SIZE_DECORATION );
4446         g_object_set( act, "short_label", _("Assign"), NULL );
4447         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4448         g_object_set_data( holder, "set_action", act );
4449         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4450         // make sure it's disabled if we're not picking alpha
4451         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4452         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4453     }
4458 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4460     {
4461         /* Width */
4462         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4463         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4464         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4465                                                               _("Pen Width"), _("Width:"),
4466                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4467                                                               "tools.eraser", "width", 15,
4468                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4469                                                               1, 100, 1.0, 10.0,
4470                                                               labels, values, G_N_ELEMENTS(labels),
4471                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4472         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4473         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4474     }
4476     {
4477         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4479         GtkTreeIter iter;
4480         gtk_list_store_append( model, &iter );
4481         gtk_list_store_set( model, &iter,
4482                             0, _("Delete"),
4483                             1, _("Delete objects touched by the eraser"),
4484                             2, "delete_object",
4485                             -1 );
4487         gtk_list_store_append( model, &iter );
4488         gtk_list_store_set( model, &iter,
4489                             0, _("Cut"),
4490                             1, _("Cut out from objects"),
4491                             2, "difference",
4492                             -1 );
4494         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4495         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4496         g_object_set_data( holder, "eraser_mode_action", act );
4498         ege_select_one_action_set_appearance( act, "full" );
4499         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4500         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4501         ege_select_one_action_set_icon_column( act, 2 );
4502         ege_select_one_action_set_tooltip_column( act, 1  );
4504         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4505         ege_select_one_action_set_active( act, eraserMode );
4506         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4507     }
4508     
4511 //########################
4512 //##    Text Toolbox    ##
4513 //########################
4514 /*
4515 static void
4516 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4518     //Call back for letter sizing spinbutton
4521 static void
4522 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4524     //Call back for line height spinbutton
4527 static void
4528 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4530     //Call back for horizontal kerning spinbutton
4533 static void
4534 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4536     //Call back for vertical kerning spinbutton
4539 static void
4540 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4542     //Call back for letter rotation spinbutton
4543 }*/
4545 namespace {
4547 bool popdown_visible = false;
4548 bool popdown_hasfocus = false;
4550 void
4551 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4553     SPStyle *query =
4554         sp_style_new (SP_ACTIVE_DOCUMENT);
4556     int result_fontspec =
4557         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4559     int result_family =
4560         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4562     int result_style =
4563         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4565     int result_numbers =
4566         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4568     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4570     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4571     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4572     {
4573         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4575         if (repr)
4576         {
4577             sp_style_read_from_repr (query, repr);
4578         }
4579         else
4580         {
4581             return;
4582         }
4583     }
4585     if (query->text)
4586     {
4587         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4588             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4589             gtk_entry_set_text (GTK_ENTRY (entry), "");
4591         } else if (query->text->font_specification.value || query->text->font_family.value) {
4593             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4595             // Get the font that corresponds
4596             Glib::ustring familyName;
4598             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4599             if (font) {
4600                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4601                 font->Unref();
4602                 font = NULL;
4603             }
4605             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4607             Gtk::TreePath path;
4608             try {
4609                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4610             } catch (...) {
4611                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4612                 return;
4613             }
4615             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4616             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4618             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4620             gtk_tree_selection_select_path (tselection, path.gobj());
4621             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4623             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4624         }
4626         //Size
4627         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4628         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4629         g_object_set_data (tbl, "size-block", gpointer(1));
4630         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4631         g_object_set_data (tbl, "size-block", gpointer(0));
4632         free (str);
4634         //Anchor
4635         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4636         {
4637             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4638             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4639             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4640             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4641         }
4642         else
4643         {
4644             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4645             {
4646                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4647                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4648                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4649                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4650             }
4651             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4652             {
4653                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4654                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4655                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4656                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4657             }
4658             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4659             {
4660                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4661                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4662                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4663                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4664             }
4665         }
4667         //Style
4668         {
4669             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4671             gboolean active = gtk_toggle_button_get_active (button);
4672             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4674             if (active != check)
4675             {
4676                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4677                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4678                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4679             }
4680         }
4682         {
4683             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4685             gboolean active = gtk_toggle_button_get_active (button);
4686             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4688             if (active != check)
4689             {
4690                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4691                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4692                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4693             }
4694         }
4696         //Orientation
4697         //locking both buttons, changing one affect all group (both)
4698         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4699         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4701         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4702         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4704         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4705         {
4706             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4707         }
4708         else
4709         {
4710             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4711         }
4712         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4713         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4714     }
4716     sp_style_unref(query);
4719 void
4720 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4722     sp_text_toolbox_selection_changed (selection, tbl);
4725 void
4726 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4728     sp_text_toolbox_selection_changed (NULL, tbl);
4731 void
4732 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4733                                 GObject             *tbl)
4735     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4736     GtkTreeModel *model = 0;
4737     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4738     GtkTreeIter   iter;
4739     char         *family = 0;
4741     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4742     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4744     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4745         return;
4746     }
4748     gtk_tree_model_get (model, &iter, 0, &family, -1);
4750     if (g_object_get_data (G_OBJECT (selection), "block"))
4751     {
4752         gtk_entry_set_text (GTK_ENTRY (entry), family);
4753         return;
4754     }
4756     gtk_entry_set_text (GTK_ENTRY (entry), family);
4758     SPStyle *query =
4759         sp_style_new (SP_ACTIVE_DOCUMENT);
4761     int result_fontspec =
4762         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4764     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4766     SPCSSAttr *css = sp_repr_css_attr_new ();
4769     // First try to get the font spec from the stored value
4770     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4772     if (fontSpec.empty()) {
4773         // Construct a new font specification if it does not yet exist
4774         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4775         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4776         fontFromStyle->Unref();
4777     }
4779     if (!fontSpec.empty()) {
4780         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4781         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4782             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4783             if (font) {
4784                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4786                 // Set all the these just in case they were altered when finding the best
4787                 // match for the new family and old style...
4789                 gchar c[256];
4791                 font->Family(c, 256);
4792                 sp_repr_css_set_property (css, "font-family", c);
4794                 font->Attribute( "weight", c, 256);
4795                 sp_repr_css_set_property (css, "font-weight", c);
4797                 font->Attribute("style", c, 256);
4798                 sp_repr_css_set_property (css, "font-style", c);
4800                 font->Attribute("stretch", c, 256);
4801                 sp_repr_css_set_property (css, "font-stretch", c);
4803                 font->Attribute("variant", c, 256);
4804                 sp_repr_css_set_property (css, "font-variant", c);
4806                 font->Unref();
4807             }
4808         }
4809     }
4811     // If querying returned nothing, set the default style of the tool (for new texts)
4812     if (result_fontspec == QUERY_STYLE_NOTHING)
4813     {
4814         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4815         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4816     }
4817     else
4818     {
4819         sp_desktop_set_style (desktop, css, true, true);
4820     }
4822     sp_style_unref(query);
4824     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4825                                    _("Text: Change font family"));
4826     sp_repr_css_attr_unref (css);
4827     free (family);
4828     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4830     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4833 void
4834 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4835                                        GObject      *tbl)
4837     const char *family = gtk_entry_get_text (entry);
4839     try {
4840         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4841         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4842         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4843         gtk_tree_selection_select_path (selection, path.gobj());
4844         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4845         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4846     } catch (...) {
4847         if (family && strlen (family))
4848         {
4849             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4850         }
4851     }
4854 void
4855 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4856                                    gpointer          data)
4858     if (g_object_get_data (G_OBJECT (button), "block")) return;
4859     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4860     int prop = GPOINTER_TO_INT(data);
4862     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4863     SPCSSAttr *css = sp_repr_css_attr_new ();
4865     switch (prop)
4866     {
4867         case 0:
4868         {
4869             sp_repr_css_set_property (css, "text-anchor", "start");
4870             sp_repr_css_set_property (css, "text-align", "start");
4871             break;
4872         }
4873         case 1:
4874         {
4875             sp_repr_css_set_property (css, "text-anchor", "middle");
4876             sp_repr_css_set_property (css, "text-align", "center");
4877             break;
4878         }
4880         case 2:
4881         {
4882             sp_repr_css_set_property (css, "text-anchor", "end");
4883             sp_repr_css_set_property (css, "text-align", "end");
4884             break;
4885         }
4887         case 3:
4888         {
4889             sp_repr_css_set_property (css, "text-anchor", "start");
4890             sp_repr_css_set_property (css, "text-align", "justify");
4891             break;
4892         }
4893     }
4895     SPStyle *query =
4896         sp_style_new (SP_ACTIVE_DOCUMENT);
4897     int result_numbers =
4898         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4900     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4901     if (result_numbers == QUERY_STYLE_NOTHING)
4902     {
4903         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4904     }
4906     sp_style_unref(query);
4908     sp_desktop_set_style (desktop, css, true, true);
4909     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4910                                    _("Text: Change alignment"));
4911     sp_repr_css_attr_unref (css);
4913     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4916 void
4917 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4918                                gpointer          data)
4920     if (g_object_get_data (G_OBJECT (button), "block")) return;
4922     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4923     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4924     int          prop       = GPOINTER_TO_INT(data);
4925     bool         active     = gtk_toggle_button_get_active (button);
4927     SPStyle *query =
4928         sp_style_new (SP_ACTIVE_DOCUMENT);
4930     int result_fontspec =
4931         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4933     int result_family =
4934         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4936     int result_style =
4937         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4939     int result_numbers =
4940         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4942     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4943     Glib::ustring newFontSpec = "";
4945     if (fontSpec.empty()) {
4946         // Construct a new font specification if it does not yet exist
4947         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4948         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4949         fontFromStyle->Unref();
4950     }
4952     switch (prop)
4953     {
4954         case 0:
4955         {
4956             if (!fontSpec.empty()) {
4957                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4958             }
4959             if (fontSpec != newFontSpec) {
4960                 // Don't even set the bold if the font didn't exist on the system
4961                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4962             }
4963             break;
4964         }
4966         case 1:
4967         {
4968             if (!fontSpec.empty()) {
4969                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4970             }
4971             if (fontSpec != newFontSpec) {
4972                 // Don't even set the italic if the font didn't exist on the system
4973                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4974             }
4975             break;
4976         }
4977     }
4979     if (!newFontSpec.empty()) {
4980         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4981     }
4983     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4984     if (result_fontspec == QUERY_STYLE_NOTHING)
4985     {
4986         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4987     }
4989     sp_style_unref(query);
4991     sp_desktop_set_style (desktop, css, true, true);
4992     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4993                                    _("Text: Change font style"));
4994     sp_repr_css_attr_unref (css);
4996     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4999 void
5000 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5001                                      gpointer         data)
5003     if (g_object_get_data (G_OBJECT (button), "block")) {
5004         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5005         return;
5006     }
5008     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5009     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5010     int          prop       = GPOINTER_TO_INT(data);
5012     switch (prop)
5013     {
5014         case 0:
5015         {
5016             sp_repr_css_set_property (css, "writing-mode", "lr");
5017             break;
5018         }
5020         case 1:
5021         {
5022             sp_repr_css_set_property (css, "writing-mode", "tb");
5023             break;
5024         }
5025     }
5027     SPStyle *query =
5028         sp_style_new (SP_ACTIVE_DOCUMENT);
5029     int result_numbers =
5030         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5032     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5033     if (result_numbers == QUERY_STYLE_NOTHING)
5034     {
5035         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5036     }
5038     sp_desktop_set_style (desktop, css, true, true);
5039     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5040                                    _("Text: Change orientation"));
5041     sp_repr_css_attr_unref (css);
5043     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5046 gboolean
5047 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5049     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5050     if (!desktop) return FALSE;
5052     switch (get_group0_keyval (event)) {
5053         case GDK_Escape: // defocus
5054             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5055             sp_text_toolbox_selection_changed (NULL, tbl); // update
5056             return TRUE; // I consumed the event
5057             break;
5058     }
5059     return FALSE;
5062 gboolean
5063 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5065     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5066     if (!desktop) return FALSE;
5068     switch (get_group0_keyval (event)) {
5069         case GDK_KP_Enter:
5070         case GDK_Return:
5071         case GDK_Escape: // defocus
5072             gtk_widget_hide (w);
5073             popdown_visible = false;
5074             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5075             return TRUE; // I consumed the event
5076             break;
5077         case GDK_w:
5078         case GDK_W:
5079             if (event->state & GDK_CONTROL_MASK) {
5080                 gtk_widget_hide (w);
5081                 popdown_visible = false;
5082                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5083                 return TRUE; // I consumed the event
5084             }
5085             break;
5086     }
5087     return FALSE;
5091 void
5092 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5093                                GObject     *tbl)
5095     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5097     if (g_object_get_data (tbl, "size-block")) return;
5099     // If this is not from selecting a size in the list (in which case get_active will give the
5100     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5101     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5102     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5103     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5104         return;
5106     gchar *endptr;
5107     gdouble value = -1;
5108     char *text = gtk_combo_box_get_active_text (cbox);
5109     if (text) {
5110         value = g_strtod (text, &endptr);
5111         if (endptr == text) // conversion failed, non-numeric input
5112             value = -1;
5113         free (text);
5114     }
5115     if (value <= 0) {
5116         return; // could not parse value
5117     }
5119     SPCSSAttr *css = sp_repr_css_attr_new ();
5120     Inkscape::CSSOStringStream osfs;
5121     osfs << value;
5122     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5124     SPStyle *query =
5125         sp_style_new (SP_ACTIVE_DOCUMENT);
5126     int result_numbers =
5127         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5129     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5130     if (result_numbers == QUERY_STYLE_NOTHING)
5131     {
5132         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5133     }
5135     sp_style_unref(query);
5137     sp_desktop_set_style (desktop, css, true, true);
5138     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5139                                    _("Text: Change font size"));
5140     sp_repr_css_attr_unref (css);
5142     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5145 gboolean
5146 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5148     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5149     if (!desktop) return FALSE;
5151     if (!g_object_get_data (tbl, "esc-pressed")) {
5152         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5153         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5154         sp_text_toolbox_size_changed (cbox, tbl);
5155         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5156     }
5157     return FALSE; // I consumed the event
5161 gboolean
5162 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5164     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5165     if (!desktop) return FALSE;
5167     switch (get_group0_keyval (event)) {
5168         case GDK_Escape: // defocus
5169             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5170             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5171             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5172             return TRUE; // I consumed the event
5173             break;
5174         case GDK_Return: // defocus
5175         case GDK_KP_Enter:
5176             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5177             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5178             sp_text_toolbox_size_changed (cbox, tbl);
5179             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5180             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5181             return TRUE; // I consumed the event
5182             break;
5183     }
5184     return FALSE;
5187 void
5188 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5189                                          GObject            *tbl)
5191     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5192     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5193     int x, y;
5195     if (!popdown_visible)
5196     {
5197         gdk_window_get_origin (widget->window, &x, &y);
5198         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5199         gtk_widget_show_all (popdown);
5200         //sp_transientize (popdown);
5202         gdk_pointer_grab (widget->window, TRUE,
5203                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5204                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5205                                         GDK_POINTER_MOTION_MASK),
5206                           NULL, NULL, GDK_CURRENT_TIME);
5208         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5210         popdown_visible = true;
5211     }
5212     else
5213     {
5214         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5215         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5216         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5217         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5218         gtk_widget_hide (popdown);
5219         popdown_visible = false;
5220     }
5223 gboolean
5224 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5225                                  GdkEventFocus    */*event*/,
5226                                  GObject          */*tbl*/)
5228     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5229     return FALSE;
5232 gboolean
5233 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5234                                    GdkEventFocus    */*event*/,
5235                                    GObject          */*tbl*/)
5237     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5239     if (popdown_hasfocus) {
5240         gtk_widget_hide (popdown);
5241         popdown_hasfocus = false;
5242         popdown_visible = false;
5243         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5244         return TRUE;
5245     }
5246     return FALSE;
5249 gboolean
5250 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5251                                    GdkEventFocus    */*event*/,
5252                                    GObject          */*tbl*/)
5254     popdown_hasfocus = true;
5255     return TRUE;
5259 void
5260 cell_data_func  (GtkTreeViewColumn */*column*/,
5261                  GtkCellRenderer   *cell,
5262                  GtkTreeModel      *tree_model,
5263                  GtkTreeIter       *iter,
5264                  gpointer           /*data*/)
5266     char        *family,
5267         *family_escaped,
5268         *sample_escaped;
5270     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5272     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5274     family_escaped = g_markup_escape_text (family, -1);
5275     sample_escaped = g_markup_escape_text (sample, -1);
5277     std::stringstream markup;
5278     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5279     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5281     free (family);
5282     free (family_escaped);
5283     free (sample_escaped);
5286 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5287     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5288     if (completion) {
5289         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5290         g_object_unref (completion);
5291     }
5294 GtkWidget*
5295 sp_text_toolbox_new (SPDesktop *desktop)
5297     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5298     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5300     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5301     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5303     GtkTooltips *tt = gtk_tooltips_new();
5304     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5306     ////////////Family
5307     //Window
5308     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5309     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5311     //Entry
5312     GtkWidget           *entry = gtk_entry_new ();
5313     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5314     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5315     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5316     gtk_entry_completion_set_text_column (completion, 0);
5317     gtk_entry_completion_set_minimum_key_length (completion, 1);
5318     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5319     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5320     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5321     gtk_toolbar_append_widget( tbl, entry, "", "" );
5322     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5324     //Button
5325     GtkWidget   *button = gtk_button_new ();
5326     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5327     gtk_toolbar_append_widget( tbl, button, "", "");
5329     //Popdown
5330     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5331     GtkWidget           *treeview = gtk_tree_view_new ();
5333     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5334     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5335     gtk_tree_view_column_pack_start (column, cell, FALSE);
5336     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5337     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5338     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5340     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5341     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5342     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5344     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5346     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5347     gtk_container_add (GTK_CONTAINER (sw), treeview);
5349     gtk_container_add (GTK_CONTAINER (window), sw);
5350     gtk_widget_set_size_request (window, 300, 450);
5352     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5353     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5354     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5356     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5358     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5359     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5360     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5362     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5363     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5365     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5366     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5367     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5368     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5369     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5371     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5372     GtkWidget *box = gtk_event_box_new ();
5373     gtk_container_add (GTK_CONTAINER (box), image);
5374     gtk_toolbar_append_widget( tbl, box, "", "");
5375     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5376     GtkTooltips *tooltips = gtk_tooltips_new ();
5377     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5378     gtk_widget_hide (GTK_WIDGET (box));
5379     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5381     ////////////Size
5382     const char *sizes[] = {
5383         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5384         "16", "18", "20", "22", "24", "28",
5385         "32", "36", "40", "48", "56", "64", "72", "144"
5386     };
5388     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5389     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5390     gtk_widget_set_size_request (cbox, 80, -1);
5391     gtk_toolbar_append_widget( tbl, cbox, "", "");
5392     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5393     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5394     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5395     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5397     ////////////Text anchor
5398     GtkWidget *group   = gtk_radio_button_new (NULL);
5399     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5400     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5402     // left
5403     GtkWidget *rbutton = group;
5404     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5405     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5406     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5408     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5409     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5410     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5411     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5413     // center
5414     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5415     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5416     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5417     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5419     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5420     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5421     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5422     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5424     // right
5425     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5426     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5427     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5428     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5430     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5431     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5432     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5433     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5435     // fill
5436     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5437     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5438     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5439     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5441     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5442     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5443     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5444     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5446     gtk_toolbar_append_widget( tbl, row, "", "");
5448     //spacer
5449     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5451     ////////////Text style
5452     row = gtk_hbox_new (FALSE, 4);
5454     // bold
5455     rbutton = gtk_toggle_button_new ();
5456     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5457     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5458     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5459     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5461     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5462     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5463     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5465     // italic
5466     rbutton = gtk_toggle_button_new ();
5467     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5468     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5469     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5470     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5472     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5473     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5474     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5476     gtk_toolbar_append_widget( tbl, row, "", "");
5478     //spacer
5479     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5481     ////////////Text orientation
5482     group   = gtk_radio_button_new (NULL);
5483     row     = gtk_hbox_new (FALSE, 4);
5484     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5486     // horizontal
5487     rbutton = group;
5488     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5489     gtk_container_add           (GTK_CONTAINER (rbutton),
5490                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5491     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5492     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5494     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5495     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5496     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5498     // vertical
5499     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5500     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5501     gtk_container_add           (GTK_CONTAINER (rbutton),
5502                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5503     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5504     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5506     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5507     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5508     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5509     gtk_toolbar_append_widget( tbl, row, "", "" );
5512     //watch selection
5513     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5515     sigc::connection *c_selection_changed =
5516         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5517                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5518     pool->add_connection ("selection-changed", c_selection_changed);
5520     sigc::connection *c_selection_modified =
5521         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5522                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5523     pool->add_connection ("selection-modified", c_selection_modified);
5525     sigc::connection *c_subselection_changed =
5526         new sigc::connection (desktop->connectToolSubselectionChanged
5527                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5528     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5530     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5533     gtk_widget_show_all( GTK_WIDGET(tbl) );
5535     return GTK_WIDGET(tbl);
5536 } // end of sp_text_toolbox_new()
5538 }//<unnamed> namespace
5541 //#########################
5542 //##      Connector      ##
5543 //#########################
5545 static void sp_connector_path_set_avoid(void)
5547     cc_selection_set_avoid(true);
5551 static void sp_connector_path_set_ignore(void)
5553     cc_selection_set_avoid(false);
5558 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5560     // quit if run by the _changed callbacks
5561     if (g_object_get_data( tbl, "freeze" )) {
5562         return;
5563     }
5565     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5566     SPDocument *doc = sp_desktop_document(desktop);
5568     if (!sp_document_get_undo_sensitive(doc))
5569     {
5570         return;
5571     }
5573     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5575     if ( repr->attribute("inkscape:connector-spacing") ) {
5576         gdouble priorValue = gtk_adjustment_get_value(adj);
5577         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5578         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5579             return;
5580         }
5581     } else if ( adj->value == defaultConnSpacing ) {
5582         return;
5583     }
5585     // in turn, prevent callbacks from responding
5586     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5588     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5589     SP_OBJECT(desktop->namedview)->updateRepr();
5591     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5592     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5593         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5594         NR::Matrix m = NR::identity();
5595         avoid_item_move(&m, item);
5596     }
5598     if (items) {
5599         g_slist_free(items);
5600     }
5602     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5603             _("Change connector spacing"));
5605     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5607     spinbutton_defocus(GTK_OBJECT(tbl));
5610 static void sp_connector_graph_layout(void)
5612     if (!SP_ACTIVE_DESKTOP) return;
5614     // hack for clones, see comment in align-and-distribute.cpp
5615     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5616     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5618     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5620     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5622     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5625 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5627     if ( gtk_toggle_action_get_active( act ) ) {
5628         prefs_set_string_attribute("tools.connector", "directedlayout",
5629                 "true");
5630     } else {
5631         prefs_set_string_attribute("tools.connector", "directedlayout",
5632                 "false");
5633     }
5636 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5638     if ( gtk_toggle_action_get_active( act ) ) {
5639         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5640                 "true");
5641     } else {
5642         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5643                 "false");
5644     }
5648 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5650     prefs_set_double_attribute("tools.connector", "length", adj->value);
5651     spinbutton_defocus(GTK_OBJECT(tbl));
5654 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5655                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5656                                             bool /*is_interactive*/, gpointer data)
5658     GtkWidget *tbl = GTK_WIDGET(data);
5660     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5661         return;
5662     }
5663     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5664         return;
5665     }
5667     GtkAdjustment *adj = (GtkAdjustment*)
5668             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5669     gdouble spacing = defaultConnSpacing;
5670     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5672     gtk_adjustment_set_value(adj, spacing);
5676 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5677     NULL, /* child_added */
5678     NULL, /* child_removed */
5679     connector_tb_event_attr_changed,
5680     NULL, /* content_changed */
5681     NULL  /* order_changed */
5682 };
5685 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5687     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5689     {
5690         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5691                                           _("Avoid"),
5692                                           _("Make connectors avoid selected objects"),
5693                                           "connector_avoid",
5694                                           secondarySize );
5695         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5696         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5697     }
5699     {
5700         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5701                                           _("Ignore"),
5702                                           _("Make connectors ignore selected objects"),
5703                                           "connector_ignore",
5704                                           secondarySize );
5705         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5706         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5707     }
5709     EgeAdjustmentAction* eact = 0;
5711     // Spacing spinbox
5712     eact = create_adjustment_action( "ConnectorSpacingAction",
5713                                      _("Connector Spacing"), _("Spacing:"),
5714                                      _("The amount of space left around objects by auto-routing connectors"),
5715                                      "tools.connector", "spacing", defaultConnSpacing,
5716                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5717                                      0, 100, 1.0, 10.0,
5718                                      0, 0, 0,
5719                                      connector_spacing_changed, 1, 0 );
5720     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5722     // Graph (connector network) layout
5723     {
5724         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5725                                           _("Graph"),
5726                                           _("Nicely arrange selected connector network"),
5727                                           "graph_layout",
5728                                           secondarySize );
5729         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5730         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5731     }
5733     // Default connector length spinbox
5734     eact = create_adjustment_action( "ConnectorLengthAction",
5735                                      _("Connector Length"), _("Length:"),
5736                                      _("Ideal length for connectors when layout is applied"),
5737                                      "tools.connector", "length", 100,
5738                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5739                                      10, 1000, 10.0, 100.0,
5740                                      0, 0, 0,
5741                                      connector_length_changed, 1, 0 );
5742     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5745     // Directed edges toggle button
5746     {
5747         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5748                                                       _("Downwards"),
5749                                                       _("Make connectors with end-markers (arrows) point downwards"),
5750                                                       "directed_graph",
5751                                                       Inkscape::ICON_SIZE_DECORATION );
5752         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5754         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5755         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5756                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5758         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5759     }
5761     // Avoid overlaps toggle button
5762     {
5763         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5764                                                       _("Remove overlaps"),
5765                                                       _("Do not allow overlapping shapes"),
5766                                                       "remove_overlaps",
5767                                                       Inkscape::ICON_SIZE_DECORATION );
5768         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5770         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5771         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5772                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5774         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5775     }
5777     // Code to watch for changes to the connector-spacing attribute in
5778     // the XML.
5779     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5780     g_assert(repr != NULL);
5782     purge_repr_listener( holder, holder );
5784     if (repr) {
5785         g_object_set_data( holder, "repr", repr );
5786         Inkscape::GC::anchor(repr);
5787         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5788         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5789     }
5790 } // end of sp_connector_toolbox_prep()
5793 //#########################
5794 //##     Paintbucket     ##
5795 //#########################
5797 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5799     gint channels = ege_select_one_action_get_active( act );
5800     flood_channels_set_channels( channels );
5803 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5805     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5808 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5810     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5813 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5815     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5816     SPUnit const *unit = tracker->getActiveUnit();
5818     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5820     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5823 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5825     // FIXME: make defaults settable via Inkscape Options
5826     struct KeyValue {
5827         char const *key;
5828         double value;
5829     } const key_values[] = {
5830         {"threshold", 15},
5831         {"offset", 0.0}
5832     };
5834     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5835         KeyValue const &kv = key_values[i];
5836         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5837         if ( adj ) {
5838             gtk_adjustment_set_value(adj, kv.value);
5839         }
5840     }
5842     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5843     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5844     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5845     ege_select_one_action_set_active( autogap_action, 0 );
5848 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5850     EgeAdjustmentAction* eact = 0;
5852     {
5853         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5855         GList* items = 0;
5856         gint count = 0;
5857         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5858         {
5859             GtkTreeIter iter;
5860             gtk_list_store_append( model, &iter );
5861             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5862             count++;
5863         }
5864         g_list_free( items );
5865         items = 0;
5866         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5867         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5868         ege_select_one_action_set_appearance( act1, "compact" );
5869         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5870         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5871         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5872         g_object_set_data( holder, "channels_action", act1 );
5873     }
5875     // Spacing spinbox
5876     {
5877         eact = create_adjustment_action(
5878             "ThresholdAction",
5879             _("Fill Threshold"), _("Threshold:"),
5880             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5881             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5882             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5883             0, 0, 0,
5884             paintbucket_threshold_changed, 1, 0 );
5886         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5887     }
5889     // Create the units menu.
5890     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5891     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5892     if (stored_unit)
5893         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5894     g_object_set_data( holder, "tracker", tracker );
5895     {
5896         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5897         gtk_action_group_add_action( mainActions, act );
5898     }
5900     // Offset spinbox
5901     {
5902         eact = create_adjustment_action(
5903             "OffsetAction",
5904             _("Grow/shrink by"), _("Grow/shrink by:"),
5905             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5906             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5907             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5908             0, 0, 0,
5909             paintbucket_offset_changed, 1, 2);
5910         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5912         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5913     }
5915     /* Auto Gap */
5916     {
5917         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5919         GList* items = 0;
5920         gint count = 0;
5921         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5922         {
5923             GtkTreeIter iter;
5924             gtk_list_store_append( model, &iter );
5925             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5926             count++;
5927         }
5928         g_list_free( items );
5929         items = 0;
5930         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5931         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5932         ege_select_one_action_set_appearance( act2, "compact" );
5933         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5934         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5935         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5936         g_object_set_data( holder, "autogap_action", act2 );
5937     }
5939     /* Reset */
5940     {
5941         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5942                                           _("Defaults"),
5943                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5944                                           GTK_STOCK_CLEAR );
5945         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5946         gtk_action_group_add_action( mainActions, act );
5947         gtk_action_set_sensitive( act, TRUE );
5948     }
5952 /*
5953   Local Variables:
5954   mode:c++
5955   c-file-style:"stroustrup"
5956   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5957   indent-tabs-mode:nil
5958   fill-column:99
5959   End:
5960 */
5961 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :