Code

56f33bf9677660f79ab2e9b89d5c4a77f03a2aa1
[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 <gtkmm.h>
31 #include <gtk/gtk.h>
32 #include <iostream>
33 #include <sstream>
35 #include "widgets/button.h"
36 #include "widgets/widget-sizes.h"
37 #include "widgets/spw-utilities.h"
38 #include "widgets/spinbutton-events.h"
39 #include "dialogs/text-edit.h"
41 #include "ui/widget/style-swatch.h"
43 #include "prefs-utils.h"
44 #include "verbs.h"
45 #include "sp-namedview.h"
46 #include "desktop.h"
47 #include "desktop-handles.h"
48 #include "xml/repr.h"
49 #include "xml/node-event-vector.h"
50 #include <glibmm/i18n.h>
51 #include "helper/unit-menu.h"
52 #include "helper/units.h"
54 #include "inkscape.h"
55 #include "conn-avoid-ref.h"
58 #include "select-toolbar.h"
59 #include "gradient-toolbar.h"
61 #include "connector-context.h"
62 #include "node-context.h"
63 #include "shape-editor.h"
64 #include "tweak-context.h"
65 #include "sp-rect.h"
66 #include "box3d.h"
67 #include "box3d-context.h"
68 #include "sp-star.h"
69 #include "sp-spiral.h"
70 #include "sp-ellipse.h"
71 #include "sp-text.h"
72 #include "sp-flowtext.h"
73 #include "style.h"
74 #include "selection.h"
75 #include "selection-chemistry.h"
76 #include "document-private.h"
77 #include "desktop-style.h"
78 #include "../libnrtype/font-lister.h"
79 #include "../libnrtype/font-instance.h"
80 #include "../connection-pool.h"
81 #include "../prefs-utils.h"
82 #include "../inkscape-stock.h"
83 #include "icon.h"
84 #include "graphlayout/graphlayout.h"
86 #include "mod360.h"
88 #include "toolbox.h"
90 #include "flood-context.h"
92 #include "ink-action.h"
93 #include "ege-adjustment-action.h"
94 #include "ege-output-action.h"
95 #include "ege-select-one-action.h"
96 #include "helper/unit-tracker.h"
98 using Inkscape::UnitTracker;
100 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
101 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
103 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
116 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
122 static struct {
123     gchar const *type_name;
124     gchar const *data_name;
125     sp_verb_t verb;
126     sp_verb_t doubleclick_verb;
127 } const tools[] = {
128     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
129     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
130     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
131     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
132     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
133     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
134     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
135     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
136     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
137     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
138     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
139     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
140     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
141     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
142     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
143     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
144     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
145     { NULL, NULL, 0, 0 }
146 };
148 static struct {
149     gchar const *type_name;
150     gchar const *data_name;
151     GtkWidget *(*create_func)(SPDesktop *desktop);
152     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
153     gchar const *ui_name;
154     gint swatch_verb_id;
155     gchar const *swatch_tool;
156     gchar const *swatch_tip;
157 } const aux_toolboxes[] = {
158     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
159       SP_VERB_INVALID, 0, 0},
160     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
161       SP_VERB_INVALID, 0, 0},
162     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
163       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
164     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
165       SP_VERB_INVALID, 0, 0},
166     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
167       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
168     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
169       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
170     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
171       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
172     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
173       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
174     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
175       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
176     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
177       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
178     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
179       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
180     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
181       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
182     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
183       SP_VERB_INVALID, 0, 0},
184     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
187       SP_VERB_INVALID, 0, 0},
188     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
189       SP_VERB_INVALID, 0, 0},
190     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
191       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
192     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
193 };
196 static gchar const * ui_descr =
197         "<ui>"
198         "  <toolbar name='SelectToolbar'>"
199         "    <toolitem action='EditSelectAll' />"
200         "    <toolitem action='EditSelectAllInAllLayers' />"
201         "    <toolitem action='EditDeselect' />"
202         "    <separator />"
203         "    <toolitem action='ObjectRotate90CCW' />"
204         "    <toolitem action='ObjectRotate90' />"
205         "    <toolitem action='ObjectFlipHorizontally' />"
206         "    <toolitem action='ObjectFlipVertically' />"
207         "    <separator />"
208         "    <toolitem action='SelectionToBack' />"
209         "    <toolitem action='SelectionLower' />"
210         "    <toolitem action='SelectionRaise' />"
211         "    <toolitem action='SelectionToFront' />"
212         "    <separator />"
213         "    <toolitem action='XAction' />"
214         "    <toolitem action='YAction' />"
215         "    <toolitem action='WidthAction' />"
216         "    <toolitem action='LockAction' />"
217         "    <toolitem action='HeightAction' />"
218         "    <toolitem action='UnitsAction' />"
219         "    <separator />"
220         "    <toolitem action='transform_affect_label' />"
221         "    <toolitem action='transform_stroke' />"
222         "    <toolitem action='transform_corners' />"
223         "    <toolitem action='transform_gradient' />"
224         "    <toolitem action='transform_pattern' />"
225         "  </toolbar>"
227         "  <toolbar name='NodeToolbar'>"
228         "    <toolitem action='NodeInsertAction' />"
229         "    <toolitem action='NodeDeleteAction' />"
230         "    <separator />"
231         "    <toolitem action='NodeJoinAction' />"
232         "    <toolitem action='NodeJoinSegmentAction' />"
233         "    <toolitem action='NodeDeleteSegmentAction' />"
234         "    <toolitem action='NodeBreakAction' />"
235         "    <separator />"
236         "    <toolitem action='NodeCuspAction' />"
237         "    <toolitem action='NodeSmoothAction' />"
238         "    <toolitem action='NodeSymmetricAction' />"
239         "    <separator />"
240         "    <toolitem action='NodeLineAction' />"
241         "    <toolitem action='NodeCurveAction' />"
242         "    <separator />"
243         "    <toolitem action='ObjectToPath' />"
244         "    <toolitem action='StrokeToPath' />"
245         "    <separator />"
246         "    <toolitem action='NodesShowHandlesAction' />"
247         "    <separator />"
248         "    <toolitem action='EditNextLPEParameterAction' />"
249         "    <separator />"
250         "    <toolitem action='NodeXAction' />"
251         "    <toolitem action='NodeYAction' />"
252         "    <toolitem action='NodeUnitsAction' />"
253         "  </toolbar>"
255         "  <toolbar name='TweakToolbar'>"
256         "    <toolitem action='TweakWidthAction' />"
257         "    <separator />"
258         "    <toolitem action='TweakForceAction' />"
259         "    <toolitem action='TweakPressureAction' />"
260         "    <separator />"
261         "    <toolitem action='TweakModeAction' />"
262         "    <separator />"
263         "    <toolitem action='TweakFidelityAction' />"
264         "    <separator />"
265         "    <toolitem action='TweakChannelsLabel' />"
266         "    <toolitem action='TweakDoH' />"
267         "    <toolitem action='TweakDoS' />"
268         "    <toolitem action='TweakDoL' />"
269         "    <toolitem action='TweakDoO' />"
270         "  </toolbar>"
272         "  <toolbar name='ZoomToolbar'>"
273         "    <toolitem action='ZoomIn' />"
274         "    <toolitem action='ZoomOut' />"
275         "    <separator />"
276         "    <toolitem action='Zoom1:0' />"
277         "    <toolitem action='Zoom1:2' />"
278         "    <toolitem action='Zoom2:1' />"
279         "    <separator />"
280         "    <toolitem action='ZoomSelection' />"
281         "    <toolitem action='ZoomDrawing' />"
282         "    <toolitem action='ZoomPage' />"
283         "    <toolitem action='ZoomPageWidth' />"
284         "    <separator />"
285         "    <toolitem action='ZoomPrev' />"
286         "    <toolitem action='ZoomNext' />"
287         "  </toolbar>"
289         "  <toolbar name='StarToolbar'>"
290         "    <separator />"
291         "    <toolitem action='StarStateAction' />"
292         "    <separator />"
293         "    <toolitem action='FlatAction' />"
294         "    <separator />"
295         "    <toolitem action='MagnitudeAction' />"
296         "    <toolitem action='SpokeAction' />"
297         "    <toolitem action='RoundednessAction' />"
298         "    <toolitem action='RandomizationAction' />"
299         "    <separator />"
300         "    <toolitem action='StarResetAction' />"
301         "  </toolbar>"
303         "  <toolbar name='RectToolbar'>"
304         "    <toolitem action='RectStateAction' />"
305         "    <toolitem action='RectWidthAction' />"
306         "    <toolitem action='RectHeightAction' />"
307         "    <toolitem action='RadiusXAction' />"
308         "    <toolitem action='RadiusYAction' />"
309         "    <toolitem action='RectUnitsAction' />"
310         "    <separator />"
311         "    <toolitem action='RectResetAction' />"
312         "  </toolbar>"
314         "  <toolbar name='3DBoxToolbar'>"
315         "    <toolitem action='3DBoxAngleXAction' />"
316         "    <toolitem action='3DBoxVPXStateAction' />"
317         "    <separator />"
318         "    <toolitem action='3DBoxAngleYAction' />"
319         "    <toolitem action='3DBoxVPYStateAction' />"
320         "    <separator />"
321         "    <toolitem action='3DBoxAngleZAction' />"
322         "    <toolitem action='3DBoxVPZStateAction' />"
323         "  </toolbar>"
325         "  <toolbar name='SpiralToolbar'>"
326         "    <toolitem action='SpiralStateAction' />"
327         "    <toolitem action='SpiralRevolutionAction' />"
328         "    <toolitem action='SpiralExpansionAction' />"
329         "    <toolitem action='SpiralT0Action' />"
330         "    <separator />"
331         "    <toolitem action='SpiralResetAction' />"
332         "  </toolbar>"
334         "  <toolbar name='PenToolbar'>"
335         "  </toolbar>"
337         "  <toolbar name='PencilToolbar'>"
338         "  </toolbar>"
340         "  <toolbar name='CalligraphyToolbar'>"
341         "    <separator />"
342         "    <toolitem action='CalligraphyWidthAction' />"
343         "    <toolitem action='PressureAction' />"
344         "    <toolitem action='TraceAction' />"
345         "    <toolitem action='ThinningAction' />"
346         "    <separator />"
347         "    <toolitem action='AngleAction' />"
348         "    <toolitem action='TiltAction' />"
349         "    <toolitem action='FixationAction' />"
350         "    <separator />"
351         "    <toolitem action='CapRoundingAction' />"
352         "    <separator />"
353         "    <toolitem action='TremorAction' />"
354         "    <toolitem action='WiggleAction' />"
355         "    <toolitem action='MassAction' />"
356         "    <separator />"
357         "    <toolitem action='CalligraphyResetAction' />"
358         "  </toolbar>"
360         "  <toolbar name='ArcToolbar'>"
361         "    <toolitem action='ArcStateAction' />"
362         "    <separator />"
363         "    <toolitem action='ArcStartAction' />"
364         "    <toolitem action='ArcEndAction' />"
365         "    <separator />"
366         "    <toolitem action='ArcOpenAction' />"
367         "    <separator />"
368         "    <toolitem action='ArcResetAction' />"
369         "    <separator />"
370         "  </toolbar>"
372         "  <toolbar name='PaintbucketToolbar'>"
373         "    <toolitem action='ChannelsAction' />"
374         "    <separator />"
375         "    <toolitem action='ThresholdAction' />"
376         "    <separator />"
377         "    <toolitem action='OffsetAction' />"
378         "    <toolitem action='PaintbucketUnitsAction' />"
379         "    <separator />"
380         "    <toolitem action='AutoGapAction' />"
381         "    <separator />"
382         "    <toolitem action='PaintbucketResetAction' />"
383         "  </toolbar>"
385         "  <toolbar name='DropperToolbar'>"
386         "    <toolitem action='DropperPickAlphaAction' />"
387         "    <toolitem action='DropperSetAlphaAction' />"
388         "  </toolbar>"
390         "  <toolbar name='ConnectorToolbar'>"
391         "    <toolitem action='ConnectorAvoidAction' />"
392         "    <toolitem action='ConnectorIgnoreAction' />"
393         "    <toolitem action='ConnectorSpacingAction' />"
394         "    <toolitem action='ConnectorGraphAction' />"
395         "    <toolitem action='ConnectorLengthAction' />"
396         "    <toolitem action='ConnectorDirectedAction' />"
397         "    <toolitem action='ConnectorOverlapAction' />"
398         "  </toolbar>"
400         "</ui>"
403 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
405 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
407 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
408 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
410 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
411 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
413 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
414 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
416 /* Global text entry widgets necessary for update */
417 /* GtkWidget *dropper_rgb_entry,
418           *dropper_opacity_entry ; */
419 // should be made a private member once this is converted to class
421 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
422     connection->disconnect();
423     delete connection;
426 static void purge_repr_listener( GObject* obj, GObject* tbl )
428     (void)obj;
429     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
430     if (oldrepr) { // remove old listener
431         sp_repr_remove_listener_by_data(oldrepr, tbl);
432         Inkscape::GC::release(oldrepr);
433         oldrepr = 0;
434         g_object_set_data( tbl, "repr", NULL );
435     }
438 GtkWidget *
439 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
440                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
441                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
443     SPAction *action = verb->get_action(view);
444     if (!action) return NULL;
446     SPAction *doubleclick_action;
447     if (doubleclick_verb)
448         doubleclick_action = doubleclick_verb->get_action(view);
449     else
450         doubleclick_action = NULL;
452     /* fixme: Handle sensitive/unsensitive */
453     /* fixme: Implement sp_button_new_from_action */
454     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
455     gtk_widget_show(b);
456     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
458     return b;
461 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
462                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
464     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
467 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
468                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
470     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
474 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
476     SPAction* targetAction = SP_ACTION(user_data);
477     if ( targetAction ) {
478         sp_action_perform( targetAction, NULL );
479     }
482 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
484     if ( data ) {
485         GtkAction* act = GTK_ACTION(data);
486         gtk_action_set_sensitive( act, sensitive );
487     }
490 static SPActionEventVector action_event_vector = {
491     {NULL},
492     NULL,
493     NULL,
494     sp_action_action_set_sensitive,
495     NULL,
496     NULL
497 };
499 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
501     GtkAction* act = 0;
503     SPAction* targetAction = verb->get_action(view);
504     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
505     act = GTK_ACTION(inky);
506     gtk_action_set_sensitive( act, targetAction->sensitive );
508     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
510     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
511     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
513     return act;
516 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
518     Inkscape::UI::View::View *view = desktop;
519     gint verbsToUse[] = {
520         // disabled until we have icons for them:
521         //find
522         //SP_VERB_EDIT_TILE,
523         //SP_VERB_EDIT_UNTILE,
524         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
525         SP_VERB_DIALOG_DISPLAY,
526         SP_VERB_DIALOG_FILL_STROKE,
527         SP_VERB_DIALOG_NAMEDVIEW,
528         SP_VERB_DIALOG_TEXT,
529         SP_VERB_DIALOG_XML_EDITOR,
530         SP_VERB_EDIT_CLONE,
531         SP_VERB_EDIT_COPY,
532         SP_VERB_EDIT_CUT,
533         SP_VERB_EDIT_DUPLICATE,
534         SP_VERB_EDIT_PASTE,
535         SP_VERB_EDIT_REDO,
536         SP_VERB_EDIT_UNDO,
537         SP_VERB_EDIT_UNLINK_CLONE,
538         SP_VERB_FILE_EXPORT,
539         SP_VERB_FILE_IMPORT,
540         SP_VERB_FILE_NEW,
541         SP_VERB_FILE_OPEN,
542         SP_VERB_FILE_PRINT,
543         SP_VERB_FILE_SAVE,
544         SP_VERB_OBJECT_TO_CURVE,
545         SP_VERB_SELECTION_GROUP,
546         SP_VERB_SELECTION_OUTLINE,
547         SP_VERB_SELECTION_UNGROUP,
548         SP_VERB_ZOOM_1_1,
549         SP_VERB_ZOOM_1_2,
550         SP_VERB_ZOOM_2_1,
551         SP_VERB_ZOOM_DRAWING,
552         SP_VERB_ZOOM_IN,
553         SP_VERB_ZOOM_NEXT,
554         SP_VERB_ZOOM_OUT,
555         SP_VERB_ZOOM_PAGE,
556         SP_VERB_ZOOM_PAGE_WIDTH,
557         SP_VERB_ZOOM_PREV,
558         SP_VERB_ZOOM_SELECTION,
559     };
561     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
562     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
564     static std::map<SPDesktop*, GtkActionGroup*> groups;
565     GtkActionGroup* mainActions = 0;
566     if ( groups.find(desktop) != groups.end() ) {
567         mainActions = groups[desktop];
568     }
570     if ( !mainActions ) {
571         mainActions = gtk_action_group_new("main");
572         groups[desktop] = mainActions;
573     }
575     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
576         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
577         if ( verb ) {
578             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
579                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
580                 gtk_action_group_add_action( mainActions, act );
581             }
582         }
583     }
585     return mainActions;
589 GtkWidget *
590 sp_tool_toolbox_new()
592     GtkTooltips *tt = gtk_tooltips_new();
593     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
595     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
596     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
598     gtk_widget_set_sensitive(tb, FALSE);
600     GtkWidget *hb = gtk_handle_box_new();
601     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
602     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
603     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
605     gtk_container_add(GTK_CONTAINER(hb), tb);
606     gtk_widget_show(GTK_WIDGET(tb));
608     sigc::connection* conn = new sigc::connection;
609     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
611     return hb;
614 static void
615 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
617     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
618     gtk_widget_queue_resize(child);
621 static void
622 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
624     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
625     gtk_widget_queue_resize(child);
628 GtkWidget *
629 sp_aux_toolbox_new()
631     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
633     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
635     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
637     gtk_widget_set_sensitive(tb, FALSE);
639     GtkWidget *hb = gtk_handle_box_new();
640     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
641     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
642     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
644     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
645     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
647     gtk_container_add(GTK_CONTAINER(hb), tb);
648     gtk_widget_show(GTK_WIDGET(tb));
650     sigc::connection* conn = new sigc::connection;
651     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
653     return hb;
656 //####################################
657 //# Commands Bar
658 //####################################
660 GtkWidget *
661 sp_commands_toolbox_new()
663     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
665     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
667     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
668     gtk_widget_set_sensitive(tb, FALSE);
670     GtkWidget *hb = gtk_handle_box_new();
671     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
672     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
673     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
675     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
676     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
678     gtk_container_add(GTK_CONTAINER(hb), tb);
679     gtk_widget_show(GTK_WIDGET(tb));
681     sigc::connection* conn = new sigc::connection;
682     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
684     return hb;
687 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
688                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
689                                                        gchar const *path, gchar const *data, gdouble def,
690                                                        GtkWidget *focusTarget,
691                                                        GtkWidget *us,
692                                                        GObject *dataKludge,
693                                                        gboolean altx, gchar const *altx_mark,
694                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
695                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
696                                                        void (*callback)(GtkAdjustment *, GObject *),
697                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
699     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
700                                                              lower, upper, step, page, page ) );
701     if (us) {
702         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
703     }
705     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
707     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
708     if ( shortLabel ) {
709         g_object_set( act, "short_label", shortLabel, NULL );
710     }
712     if ( (descrCount > 0) && descrLabels && descrValues ) {
713         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
714     }
716     if ( focusTarget ) {
717         ege_adjustment_action_set_focuswidget( act, focusTarget );
718     }
720     if ( altx && altx_mark ) {
721         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
722     }
724     if ( dataKludge ) {
725         g_object_set_data( dataKludge, data, adj );
726     }
728     // Using a cast just to make sure we pass in the right kind of function pointer
729     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
731     return act;
735 //####################################
736 //# node editing callbacks
737 //####################################
739 /**
740  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
741  */
742 static ShapeEditor *get_current_shape_editor()
744     if (!SP_ACTIVE_DESKTOP) {
745         return NULL;
746     }
748     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
750     if (!SP_IS_NODE_CONTEXT(event_context)) {
751         return NULL;
752     }
754     return SP_NODE_CONTEXT(event_context)->shape_editor;
758 void
759 sp_node_path_edit_add(void)
761     ShapeEditor *shape_editor = get_current_shape_editor();
762     if (shape_editor) shape_editor->add_node();
765 void
766 sp_node_path_edit_delete(void)
768     ShapeEditor *shape_editor = get_current_shape_editor();
769     if (shape_editor) shape_editor->delete_nodes();
772 void
773 sp_node_path_edit_delete_segment(void)
775     ShapeEditor *shape_editor = get_current_shape_editor();
776     if (shape_editor) shape_editor->delete_segment();
779 void
780 sp_node_path_edit_break(void)
782     ShapeEditor *shape_editor = get_current_shape_editor();
783     if (shape_editor) shape_editor->break_at_nodes();
786 void
787 sp_node_path_edit_join(void)
789     ShapeEditor *shape_editor = get_current_shape_editor();
790     if (shape_editor) shape_editor->join_nodes();
793 void
794 sp_node_path_edit_join_segment(void)
796     ShapeEditor *shape_editor = get_current_shape_editor();
797     if (shape_editor) shape_editor->join_segments();
800 void
801 sp_node_path_edit_toline(void)
803     ShapeEditor *shape_editor = get_current_shape_editor();
804     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
807 void
808 sp_node_path_edit_tocurve(void)
810     ShapeEditor *shape_editor = get_current_shape_editor();
811     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
814 void
815 sp_node_path_edit_cusp(void)
817     ShapeEditor *shape_editor = get_current_shape_editor();
818     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
821 void
822 sp_node_path_edit_smooth(void)
824     ShapeEditor *shape_editor = get_current_shape_editor();
825     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
828 void
829 sp_node_path_edit_symmetrical(void)
831     ShapeEditor *shape_editor = get_current_shape_editor();
832     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
835 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
836     bool show = gtk_toggle_action_get_active( act );
837     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
838     ShapeEditor *shape_editor = get_current_shape_editor();
839     if (shape_editor) shape_editor->show_handles(show);
842 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
843     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
846 /* is called when the node selection is modified */
847 static void
848 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
850     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
851     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
852     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
853     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
855     // quit if run by the attr_changed listener
856     if (g_object_get_data( tbl, "freeze" )) {
857         return;
858     }
860     // in turn, prevent listener from responding
861     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
863     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
864     SPUnit const *unit = tracker->getActiveUnit();
866     ShapeEditor *shape_editor = get_current_shape_editor();
867     if (shape_editor && shape_editor->has_nodepath()) {
868         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
869         int n_selected = 0;
870         if (nodepath) {
871             n_selected = nodepath->numSelected();
872         }
874         if (n_selected == 0) {
875             gtk_action_set_sensitive(xact, FALSE);
876             gtk_action_set_sensitive(yact, FALSE);
877         } else {
878             gtk_action_set_sensitive(xact, TRUE);
879             gtk_action_set_sensitive(yact, TRUE);
880             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
881             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
883             if (n_selected == 1) {
884                 NR::Point sel_node = nodepath->singleSelectedCoords();
885                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
886                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
887                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
888                 }
889             } else {
890                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
891                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
892                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
893                     /* Note: Currently x and y will always have a value, even if the coordinates of the
894                        selected nodes don't coincide (in this case we use the coordinates of the center
895                        of the bounding box). So the entries are never set to zero. */
896                     // FIXME: Maybe we should clear the entry if several nodes are selected
897                     //        instead of providing a kind of average value
898                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
899                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
900                 }
901             }
902         }
903     } else {
904         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
905         gtk_action_set_sensitive(xact, FALSE);
906         gtk_action_set_sensitive(yact, FALSE);
907     }
909     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
912 static void
913 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
915     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
917     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
918     SPUnit const *unit = tracker->getActiveUnit();
920     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
921         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
922     }
924     // quit if run by the attr_changed listener
925     if (g_object_get_data( tbl, "freeze" )) {
926         return;
927     }
929     // in turn, prevent listener from responding
930     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
932     ShapeEditor *shape_editor = get_current_shape_editor();
933     if (shape_editor && shape_editor->has_nodepath()) {
934         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
935         if (!strcmp(value_name, "x")) {
936             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
937         }
938         if (!strcmp(value_name, "y")) {
939             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
940         }
941     }
943     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
946 static void
947 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
949     sp_node_path_value_changed(adj, tbl, "x");
952 static void
953 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
955     sp_node_path_value_changed(adj, tbl, "y");
958 //################################
959 //##    Node Editing Toolbox    ##
960 //################################
962 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
964     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
965     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
966     g_object_set_data( holder, "tracker", tracker );
968     {
969         InkAction* inky = ink_action_new( "NodeInsertAction",
970                                           _("Insert node"),
971                                           _("Insert new nodes into selected segments"),
972                                           "node_insert",
973                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
974         g_object_set( inky, "short_label", _("Insert"), NULL );
975         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
976         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
977     }
979     {
980         InkAction* inky = ink_action_new( "NodeDeleteAction",
981                                           _("Delete node"),
982                                           _("Delete selected nodes"),
983                                           "node_delete",
984                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
985         g_object_set( inky, "short_label", _("Delete"), NULL );
986         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
987         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
988     }
990     {
991         InkAction* inky = ink_action_new( "NodeJoinAction",
992                                           _("Join endnodes"),
993                                           _("Join selected endnodes"),
994                                           "node_join",
995                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
996         g_object_set( inky, "short_label", _("Join"), NULL );
997         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
998         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
999     }
1001     {
1002         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1003                                           _("Join Segment"),
1004                                           _("Join selected endnodes with a new segment"),
1005                                           "node_join_segment",
1006                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1007         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1008         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1009     }
1011     {
1012         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1013                                           _("Delete Segment"),
1014                                           _("Split path between two non-endpoint nodes"),
1015                                           "node_delete_segment",
1016                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1017         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1018         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1019     }
1021     {
1022         InkAction* inky = ink_action_new( "NodeBreakAction",
1023                                           _("Node Break"),
1024                                           _("Break path at selected nodes"),
1025                                           "node_break",
1026                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1027         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1028         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1029     }
1031     {
1032         InkAction* inky = ink_action_new( "NodeCuspAction",
1033                                           _("Node Cusp"),
1034                                           _("Make selected nodes corner"),
1035                                           "node_cusp",
1036                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1037         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1038         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1039     }
1041     {
1042         InkAction* inky = ink_action_new( "NodeSmoothAction",
1043                                           _("Node Smooth"),
1044                                           _("Make selected nodes smooth"),
1045                                           "node_smooth",
1046                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1047         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1048         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1049     }
1051     {
1052         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1053                                           _("Node Symmetric"),
1054                                           _("Make selected nodes symmetric"),
1055                                           "node_symmetric",
1056                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1057         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1058         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1059     }
1061     {
1062         InkAction* inky = ink_action_new( "NodeLineAction",
1063                                           _("Node Line"),
1064                                           _("Make selected segments lines"),
1065                                           "node_line",
1066                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1067         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1068         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1069     }
1071     {
1072         InkAction* inky = ink_action_new( "NodeCurveAction",
1073                                           _("Node Curve"),
1074                                           _("Make selected segments curves"),
1075                                           "node_curve",
1076                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1077         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1078         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1079     }
1081     {
1082         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1083                                                       _("Show Handles"),
1084                                                       _("Show the Bezier handles of selected nodes"),
1085                                                       "nodes_show_handles",
1086                                                       Inkscape::ICON_SIZE_DECORATION );
1087         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1088         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1089         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1090     }
1092     {
1093         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1094                                           _("Next Path Effect Parameter"),
1095                                           _("Show next Path Effect parameter for editing"),
1096                                           "edit_next_parameter",
1097                                           Inkscape::ICON_SIZE_DECORATION );
1098         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1099         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1100     }
1102     /* X coord of selected node(s) */
1103     {
1104         EgeAdjustmentAction* eact = 0;
1105         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1106         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1107         eact = create_adjustment_action( "NodeXAction",
1108                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1109                                          "tools.nodes", "Xcoord", 0,
1110                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1111                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1112                                          labels, values, G_N_ELEMENTS(labels),
1113                                          sp_node_path_x_value_changed );
1114         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1115         g_object_set_data( holder, "nodes_x_action", eact );
1116         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1117         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1118     }
1120     /* Y coord of selected node(s) */
1121     {
1122         EgeAdjustmentAction* eact = 0;
1123         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1124         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1125         eact = create_adjustment_action( "NodeYAction",
1126                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1127                                          "tools.nodes", "Ycoord", 0,
1128                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1129                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1130                                          labels, values, G_N_ELEMENTS(labels),
1131                                          sp_node_path_y_value_changed );
1132         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1133         g_object_set_data( holder, "nodes_y_action", eact );
1134         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1135         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1136     }
1138     // add the units menu
1139     {
1140         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1141         gtk_action_group_add_action( mainActions, act );
1142     }
1144     sigc::connection *connection = new sigc::connection (
1145         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1146         );
1148     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1149     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1150 } // end of sp_node_toolbox_prep()
1153 //########################
1154 //##    Zoom Toolbox    ##
1155 //########################
1157 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1159     // no custom GtkAction setup needed
1160 } // end of sp_zoom_toolbox_prep()
1162 void
1163 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1165     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")));
1169 void
1170 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1172     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_aux_toolbox, update_aux_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1175 void
1176 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1178     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_commands_toolbox, update_commands_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1181 static void
1182 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1184     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1185     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1187     if (old_desktop) {
1188         GList *children, *iter;
1190         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1191         for ( iter = children ; iter ; iter = iter->next ) {
1192             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1193         }
1194         g_list_free(children);
1195     }
1197     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1199     if (desktop) {
1200         gtk_widget_set_sensitive(toolbox, TRUE);
1201         setup_func(toolbox, desktop);
1202         update_func(desktop, desktop->event_context, toolbox);
1203         *conn = desktop->connectEventContextChanged
1204             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1205     } else {
1206         gtk_widget_set_sensitive(toolbox, FALSE);
1207     }
1209 } // end of toolbox_set_desktop()
1212 static void
1213 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1215     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1216     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1217     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1218         // "toolbox.tools" was not set. Fallback to older value
1219         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1221         // Copy the setting forwards
1222         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1223     }
1224     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1226     for (int i = 0 ; tools[i].type_name ; i++ ) {
1227         GtkWidget *button =
1228             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1229                                                               SP_BUTTON_TYPE_TOGGLE,
1230                                                               Inkscape::Verb::get(tools[i].verb),
1231                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1232                                                               desktop,
1233                                                               tooltips );
1235         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1236                            (gpointer)button );
1237     }
1241 static void
1242 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1244     gchar const *const tname = ( eventcontext
1245                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1246                                  : NULL );
1247     for (int i = 0 ; tools[i].type_name ; i++ ) {
1248         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1249         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1250     }
1253 static void
1254 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1256     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1257     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1258     GtkUIManager* mgr = gtk_ui_manager_new();
1259     GError* errVal = 0;
1260     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1261     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1263     std::map<std::string, GtkWidget*> dataHolders;
1265     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1266         if ( aux_toolboxes[i].prep_func ) {
1267             // converted to GtkActions and UIManager
1269             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1270             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1271             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1272             dataHolders[aux_toolboxes[i].type_name] = kludge;
1273             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1274         } else {
1276             GtkWidget *sub_toolbox = 0;
1277             if (aux_toolboxes[i].create_func == NULL)
1278                 sub_toolbox = sp_empty_toolbox_new(desktop);
1279             else {
1280                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1281             }
1283             gtk_size_group_add_widget( grouper, sub_toolbox );
1285             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1286             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1288         }
1289     }
1291     // Second pass to create toolbars *after* all GtkActions are created
1292     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1293         if ( aux_toolboxes[i].prep_func ) {
1294             // converted to GtkActions and UIManager
1296             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1298             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1299             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1301             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1302             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1303             g_free( tmp );
1304             tmp = 0;
1306             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1307             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1308             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1309                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1310             }
1311             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1314             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1316             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1317                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1318                 swatch->setDesktop( desktop );
1319                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1320                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1321                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1322                 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 );
1323             }
1325             gtk_widget_show_all( holder );
1326             sp_set_font_size_smaller( holder );
1328             gtk_size_group_add_widget( grouper, holder );
1330             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1331             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1332         }
1333     }
1335     g_object_unref( G_OBJECT(grouper) );
1338 static void
1339 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1341     gchar const *tname = ( eventcontext
1342                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1343                            : NULL );
1344     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1345         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1346         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1347             gtk_widget_show_all(sub_toolbox);
1348             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1349         } else {
1350             gtk_widget_hide(sub_toolbox);
1351         }
1352     }
1355 static void
1356 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1358     gchar const * descr =
1359         "<ui>"
1360         "  <toolbar name='CommandsToolbar'>"
1361         "    <toolitem action='FileNew' />"
1362         "    <toolitem action='FileOpen' />"
1363         "    <toolitem action='FileSave' />"
1364         "    <toolitem action='FilePrint' />"
1365         "    <separator />"
1366         "    <toolitem action='FileImport' />"
1367         "    <toolitem action='FileExport' />"
1368         "    <separator />"
1369         "    <toolitem action='EditUndo' />"
1370         "    <toolitem action='EditRedo' />"
1371         "    <separator />"
1372         "    <toolitem action='EditCopy' />"
1373         "    <toolitem action='EditCut' />"
1374         "    <toolitem action='EditPaste' />"
1375         "    <separator />"
1376         "    <toolitem action='ZoomSelection' />"
1377         "    <toolitem action='ZoomDrawing' />"
1378         "    <toolitem action='ZoomPage' />"
1379         "    <separator />"
1380         "    <toolitem action='EditDuplicate' />"
1381         "    <toolitem action='EditClone' />"
1382         "    <toolitem action='EditUnlinkClone' />"
1383         "    <separator />"
1384         "    <toolitem action='SelectionGroup' />"
1385         "    <toolitem action='SelectionUnGroup' />"
1386         "    <separator />"
1387         "    <toolitem action='DialogFillStroke' />"
1388         "    <toolitem action='DialogText' />"
1389         "    <toolitem action='DialogXMLEditor' />"
1390         "    <toolitem action='DialogAlignDistribute' />"
1391         "    <separator />"
1392         "    <toolitem action='DialogPreferences' />"
1393         "    <toolitem action='DialogDocumentProperties' />"
1394         "  </toolbar>"
1395         "</ui>";
1396     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1399     GtkUIManager* mgr = gtk_ui_manager_new();
1400     GError* errVal = 0;
1402     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1403     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1405     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1406     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1407         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1408     }
1409     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1410     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1411     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1414     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1417 static void
1418 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1422 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1424     gtk_widget_show(toolbox_toplevel);
1425     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1427     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1428     if (!shown_toolbox) {
1429         return;
1430     }
1431     gtk_widget_show(toolbox);
1433     gtk_widget_show_all(shown_toolbox);
1436 void
1437 aux_toolbox_space(GtkWidget *tb, gint space)
1439     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1442 static GtkWidget *
1443 sp_empty_toolbox_new(SPDesktop *desktop)
1445     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1446     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1447     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1449     gtk_widget_show_all(tbl);
1450     sp_set_font_size_smaller (tbl);
1452     return tbl;
1455 // helper UI functions
1457 GtkWidget *
1458 sp_tb_spinbutton(
1459     gchar *label, gchar const *tooltip,
1460     gchar const *path, gchar const *data, gdouble def,
1461     GtkWidget *us,
1462     GtkWidget *tbl,
1463     gboolean altx, gchar const *altx_mark,
1464     gdouble lower, gdouble upper, gdouble step, gdouble page,
1465     void (*callback)(GtkAdjustment *, GtkWidget *),
1466     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1468     GtkTooltips *tt = gtk_tooltips_new();
1470     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1472     GtkWidget *l = gtk_label_new(label);
1473     gtk_widget_show(l);
1474     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1475     gtk_container_add(GTK_CONTAINER(hb), l);
1477     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1478                                       lower, upper, step, page, page);
1479     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1480     if (us)
1481         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1483     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1484     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1485     if (altx)
1486         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1487     gtk_widget_set_size_request(sb,
1488                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1489                                 AUX_SPINBUTTON_HEIGHT);
1490     gtk_widget_show(sb);
1491     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1492     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1493     gtk_container_add(GTK_CONTAINER(hb), sb);
1494     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1496     return hb;
1499 #define MODE_LABEL_WIDTH 70
1501 //########################
1502 //##       Star         ##
1503 //########################
1505 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1507     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1509     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1510         // do not remember prefs if this call is initiated by an undo change, because undoing object
1511         // creation sets bogus values to its attributes before it is deleted
1512         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1513     }
1515     // quit if run by the attr_changed listener
1516     if (g_object_get_data( dataKludge, "freeze" )) {
1517         return;
1518     }
1520     // in turn, prevent listener from responding
1521     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1523     bool modmade = false;
1525     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1526     GSList const *items = selection->itemList();
1527     for (; items != NULL; items = items->next) {
1528         if (SP_IS_STAR((SPItem *) items->data)) {
1529             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1530             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1531             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1532                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1533                                     + M_PI / (gint)adj->value));
1534             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1535             modmade = true;
1536         }
1537     }
1538     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1539                                    _("Star: Change number of corners"));
1541     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1544 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1546     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1548     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1549         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1550     }
1552     // quit if run by the attr_changed listener
1553     if (g_object_get_data( dataKludge, "freeze" )) {
1554         return;
1555     }
1557     // in turn, prevent listener from responding
1558     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1560     bool modmade = false;
1561     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1562     GSList const *items = selection->itemList();
1563     for (; items != NULL; items = items->next) {
1564         if (SP_IS_STAR((SPItem *) items->data)) {
1565             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1567             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1568             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1569             if (r2 < r1) {
1570                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1571             } else {
1572                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1573             }
1575             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1576             modmade = true;
1577         }
1578     }
1580     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1581                                    _("Star: Change spoke ratio"));
1583     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1586 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1588     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1589     bool flat = ege_select_one_action_get_active( act ) == 0;
1591     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1592         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1593                                     flat ? "true" : "false" );
1594     }
1596     // quit if run by the attr_changed listener
1597     if (g_object_get_data( dataKludge, "freeze" )) {
1598         return;
1599     }
1601     // in turn, prevent listener from responding
1602     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1604     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1605     GSList const *items = selection->itemList();
1606     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1607     bool modmade = false;
1609     if ( prop_action ) {
1610         gtk_action_set_sensitive( prop_action, !flat );
1611     }
1613     for (; items != NULL; items = items->next) {
1614         if (SP_IS_STAR((SPItem *) items->data)) {
1615             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1616             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1617             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1618             modmade = true;
1619         }
1620     }
1622     if (modmade) {
1623         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1624                          flat ? _("Make polygon") : _("Make star"));
1625     }
1627     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1630 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1632     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1634     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1635         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1636     }
1638     // quit if run by the attr_changed listener
1639     if (g_object_get_data( dataKludge, "freeze" )) {
1640         return;
1641     }
1643     // in turn, prevent listener from responding
1644     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1646     bool modmade = false;
1648     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1649     GSList const *items = selection->itemList();
1650     for (; items != NULL; items = items->next) {
1651         if (SP_IS_STAR((SPItem *) items->data)) {
1652             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1653             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1654             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1655             modmade = true;
1656         }
1657     }
1658     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1659                                    _("Star: Change rounding"));
1661     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1664 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1666     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1668     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1669         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1670     }
1672     // quit if run by the attr_changed listener
1673     if (g_object_get_data( dataKludge, "freeze" )) {
1674         return;
1675     }
1677     // in turn, prevent listener from responding
1678     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1680     bool modmade = false;
1682     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1683     GSList const *items = selection->itemList();
1684     for (; items != NULL; items = items->next) {
1685         if (SP_IS_STAR((SPItem *) items->data)) {
1686             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1687             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1688             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1689             modmade = true;
1690         }
1691     }
1692     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1693                                    _("Star: Change randomization"));
1695     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1699 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1700                                        gchar const */*old_value*/, gchar const */*new_value*/,
1701                                        bool /*is_interactive*/, gpointer data)
1703     GtkWidget *tbl = GTK_WIDGET(data);
1705     // quit if run by the _changed callbacks
1706     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1707         return;
1708     }
1710     // in turn, prevent callbacks from responding
1711     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1713     GtkAdjustment *adj = 0;
1715     if (!strcmp(name, "inkscape:randomized")) {
1716         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1717         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1718     } else if (!strcmp(name, "inkscape:rounded")) {
1719         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1720         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1721     } else if (!strcmp(name, "inkscape:flatsided")) {
1722         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1723         char const *flatsides = repr->attribute("inkscape:flatsided");
1724         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1725         if ( flatsides && !strcmp(flatsides,"false") ) {
1726             ege_select_one_action_set_active( flat_action, 1 );
1727             gtk_action_set_sensitive( prop_action, TRUE );
1728         } else {
1729             ege_select_one_action_set_active( flat_action, 0 );
1730             gtk_action_set_sensitive( prop_action, FALSE );
1731         }
1732     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1733         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1734         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1735         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1736         if (r2 < r1) {
1737             gtk_adjustment_set_value(adj, r2/r1);
1738         } else {
1739             gtk_adjustment_set_value(adj, r1/r2);
1740         }
1741     } else if (!strcmp(name, "sodipodi:sides")) {
1742         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1743         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1744     }
1746     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1750 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1752     NULL, /* child_added */
1753     NULL, /* child_removed */
1754     star_tb_event_attr_changed,
1755     NULL, /* content_changed */
1756     NULL  /* order_changed */
1757 };
1760 /**
1761  *  \param selection Should not be NULL.
1762  */
1763 static void
1764 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1766     int n_selected = 0;
1767     Inkscape::XML::Node *repr = NULL;
1769     purge_repr_listener( tbl, tbl );
1771     for (GSList const *items = selection->itemList();
1772          items != NULL;
1773          items = items->next)
1774     {
1775         if (SP_IS_STAR((SPItem *) items->data)) {
1776             n_selected++;
1777             repr = SP_OBJECT_REPR((SPItem *) items->data);
1778         }
1779     }
1781     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1783     if (n_selected == 0) {
1784         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1785     } else if (n_selected == 1) {
1786         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1788         if (repr) {
1789             g_object_set_data( tbl, "repr", repr );
1790             Inkscape::GC::anchor(repr);
1791             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1792             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1793         }
1794     } else {
1795         // FIXME: implement averaging of all parameters for multiple selected stars
1796         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1797         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1798     }
1802 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1804     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1805     // callbacks to lump all the changes for all selected objects in one undo step
1807     GtkAdjustment *adj = 0;
1809     // fixme: make settable in prefs!
1810     gint mag = 5;
1811     gdouble prop = 0.5;
1812     gboolean flat = FALSE;
1813     gdouble randomized = 0;
1814     gdouble rounded = 0;
1816     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1817     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1819     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1820     gtk_action_set_sensitive( sb2, !flat );
1822     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1823     gtk_adjustment_set_value(adj, mag);
1824     gtk_adjustment_value_changed(adj);
1826     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1827     gtk_adjustment_set_value(adj, prop);
1828     gtk_adjustment_value_changed(adj);
1830     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1831     gtk_adjustment_set_value(adj, rounded);
1832     gtk_adjustment_value_changed(adj);
1834     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1835     gtk_adjustment_set_value(adj, randomized);
1836     gtk_adjustment_value_changed(adj);
1840 void
1841 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1843     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1844     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1845     GtkWidget *l = gtk_label_new(NULL);
1846     gtk_label_set_markup(GTK_LABEL(l), title);
1847     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1848     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1849     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1853 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1855     {
1856         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1857         ege_output_action_set_use_markup( act, TRUE );
1858         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1859         g_object_set_data( holder, "mode_action", act );
1860     }
1862     {
1863         EgeAdjustmentAction* eact = 0;
1864         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1865         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1867         /* Flatsided checkbox */
1868         {
1869             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1871             GtkTreeIter iter;
1872             gtk_list_store_append( model, &iter );
1873             gtk_list_store_set( model, &iter,
1874                                 0, _("Polygon"),
1875                                 1, _("Regular polygon (with one handle) instead of a star"),
1876                                 2, "star_flat",
1877                                 -1 );
1879             gtk_list_store_append( model, &iter );
1880             gtk_list_store_set( model, &iter,
1881                                 0, _("Star"),
1882                                 1, _("Star instead of a regular polygon (with one handle)"),
1883                                 2, "star_angled",
1884                                 -1 );
1886             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1887             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1888             g_object_set_data( holder, "flat_action", act );
1890             ege_select_one_action_set_appearance( act, "full" );
1891             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1892             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1893             ege_select_one_action_set_icon_column( act, 2 );
1894             ege_select_one_action_set_tooltip_column( act, 1  );
1896             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1897             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1898         }
1900         /* Magnitude */
1901         {
1902         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1903         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1904         eact = create_adjustment_action( "MagnitudeAction",
1905                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1906                                          "tools.shapes.star", "magnitude", 3,
1907                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1908                                          3, 1024, 1, 5,
1909                                          labels, values, G_N_ELEMENTS(labels),
1910                                          sp_stb_magnitude_value_changed,
1911                                          1.0, 0 );
1912         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1913         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1914         }
1916         /* Spoke ratio */
1917         {
1918         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1919         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1920         eact = create_adjustment_action( "SpokeAction",
1921                                          _("Spoke ratio"), _("Spoke ratio:"),
1922                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1923                                          // Base radius is the same for the closest handle.
1924                                          _("Base radius to tip radius ratio"),
1925                                          "tools.shapes.star", "proportion", 0.5,
1926                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1927                                          0.01, 1.0, 0.01, 0.1,
1928                                          labels, values, G_N_ELEMENTS(labels),
1929                                          sp_stb_proportion_value_changed );
1930         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1931         g_object_set_data( holder, "prop_action", eact );
1932         }
1934         if ( !isFlatSided ) {
1935             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1936         } else {
1937             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1938         }
1940         /* Roundedness */
1941         {
1942         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1943         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1944         eact = create_adjustment_action( "RoundednessAction",
1945                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1946                                          "tools.shapes.star", "rounded", 0.0,
1947                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1948                                          -10.0, 10.0, 0.01, 0.1,
1949                                          labels, values, G_N_ELEMENTS(labels),
1950                                          sp_stb_rounded_value_changed );
1951         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1952         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1953         }
1955         /* Randomization */
1956         {
1957         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1958         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1959         eact = create_adjustment_action( "RandomizationAction",
1960                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1961                                          "tools.shapes.star", "randomized", 0.0,
1962                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1963                                          -10.0, 10.0, 0.001, 0.01,
1964                                          labels, values, G_N_ELEMENTS(labels),
1965                                          sp_stb_randomized_value_changed, 0.1, 3 );
1966         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1967         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1968         }
1969     }
1971     {
1972         /* Reset */
1973         {
1974             GtkAction* act = gtk_action_new( "StarResetAction",
1975                                              _("Defaults"),
1976                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1977                                              GTK_STOCK_CLEAR );
1978             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1979             gtk_action_group_add_action( mainActions, act );
1980             gtk_action_set_sensitive( act, TRUE );
1981         }
1982     }
1984     sigc::connection *connection = new sigc::connection(
1985         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1986         );
1987     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1988     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1992 //########################
1993 //##       Rect         ##
1994 //########################
1996 static void sp_rtb_sensitivize( GObject *tbl )
1998     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1999     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2000     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2002     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2003         gtk_action_set_sensitive( not_rounded, FALSE );
2004     } else {
2005         gtk_action_set_sensitive( not_rounded, TRUE );
2006     }
2010 static void
2011 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2012                           void (*setter)(SPRect *, gdouble))
2014     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2016     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2017     SPUnit const *unit = tracker->getActiveUnit();
2019     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2020         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2021     }
2023     // quit if run by the attr_changed listener
2024     if (g_object_get_data( tbl, "freeze" )) {
2025         return;
2026     }
2028     // in turn, prevent listener from responding
2029     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2031     bool modmade = false;
2032     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2033     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2034         if (SP_IS_RECT(items->data)) {
2035             if (adj->value != 0) {
2036                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2037             } else {
2038                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2039             }
2040             modmade = true;
2041         }
2042     }
2044     sp_rtb_sensitivize( tbl );
2046     if (modmade) {
2047         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2048                                    _("Change rectangle"));
2049     }
2051     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2054 static void
2055 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2057     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2060 static void
2061 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2063     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2066 static void
2067 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2069     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2072 static void
2073 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2075     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2080 static void
2081 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2083     GtkAdjustment *adj = 0;
2085     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2086     gtk_adjustment_set_value(adj, 0.0);
2087     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2088     gtk_adjustment_value_changed(adj);
2090     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2091     gtk_adjustment_set_value(adj, 0.0);
2092     gtk_adjustment_value_changed(adj);
2094     sp_rtb_sensitivize( obj );
2097 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2098                                        gchar const */*old_value*/, gchar const */*new_value*/,
2099                                        bool /*is_interactive*/, gpointer data)
2101     GObject *tbl = G_OBJECT(data);
2103     // quit if run by the _changed callbacks
2104     if (g_object_get_data( tbl, "freeze" )) {
2105         return;
2106     }
2108     // in turn, prevent callbacks from responding
2109     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2111     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2112     SPUnit const *unit = tracker->getActiveUnit();
2114     gpointer item = g_object_get_data( tbl, "item" );
2115     if (item && SP_IS_RECT(item)) {
2116         {
2117             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2118             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2119             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2120         }
2122         {
2123             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2124             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2125             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2126         }
2128         {
2129             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2130             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2131             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2132         }
2134         {
2135             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2136             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2137             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2138         }
2139     }
2141     sp_rtb_sensitivize( tbl );
2143     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2147 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2148     NULL, /* child_added */
2149     NULL, /* child_removed */
2150     rect_tb_event_attr_changed,
2151     NULL, /* content_changed */
2152     NULL  /* order_changed */
2153 };
2155 /**
2156  *  \param selection should not be NULL.
2157  */
2158 static void
2159 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2161     int n_selected = 0;
2162     Inkscape::XML::Node *repr = NULL;
2163     SPItem *item = NULL;
2165     if ( g_object_get_data( tbl, "repr" ) ) {
2166         g_object_set_data( tbl, "item", NULL );
2167     }
2168     purge_repr_listener( tbl, tbl );
2170     for (GSList const *items = selection->itemList();
2171          items != NULL;
2172          items = items->next) {
2173         if (SP_IS_RECT((SPItem *) items->data)) {
2174             n_selected++;
2175             item = (SPItem *) items->data;
2176             repr = SP_OBJECT_REPR(item);
2177         }
2178     }
2180     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2182     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2184     if (n_selected == 0) {
2185         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2187         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2188         gtk_action_set_sensitive(w, FALSE);
2189         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2190         gtk_action_set_sensitive(h, FALSE);
2192     } else if (n_selected == 1) {
2193         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2194         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2196         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2197         gtk_action_set_sensitive(w, TRUE);
2198         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2199         gtk_action_set_sensitive(h, TRUE);
2201         if (repr) {
2202             g_object_set_data( tbl, "repr", repr );
2203             g_object_set_data( tbl, "item", item );
2204             Inkscape::GC::anchor(repr);
2205             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2206             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2207         }
2208     } else {
2209         // FIXME: implement averaging of all parameters for multiple selected
2210         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2211         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2212         sp_rtb_sensitivize( tbl );
2213     }
2217 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2219     EgeAdjustmentAction* eact = 0;
2221     {
2222         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2223         ege_output_action_set_use_markup( act, TRUE );
2224         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2225         g_object_set_data( holder, "mode_action", act );
2226     }
2228     // rx/ry units menu: create
2229     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2230     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2231     // fixme: add % meaning per cent of the width/height
2232     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2233     g_object_set_data( holder, "tracker", tracker );
2235     /* W */
2236     {
2237         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2238         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2239         eact = create_adjustment_action( "RectWidthAction",
2240                                          _("Width"), _("W:"), _("Width of rectangle"),
2241                                          "tools.shapes.rect", "width", 0,
2242                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2243                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2244                                          labels, values, G_N_ELEMENTS(labels),
2245                                          sp_rtb_width_value_changed );
2246         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2247         g_object_set_data( holder, "width_action", eact );
2248         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2249         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2250     }
2252     /* H */
2253     {
2254         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2255         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2256         eact = create_adjustment_action( "RectHeightAction",
2257                                          _("Height"), _("H:"), _("Height of rectangle"),
2258                                          "tools.shapes.rect", "height", 0,
2259                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2260                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2261                                          labels, values, G_N_ELEMENTS(labels),
2262                                          sp_rtb_height_value_changed );
2263         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2264         g_object_set_data( holder, "height_action", eact );
2265         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2266         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2267     }
2269     /* rx */
2270     {
2271         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2272         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2273         eact = create_adjustment_action( "RadiusXAction",
2274                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2275                                          "tools.shapes.rect", "rx", 0,
2276                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2277                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2278                                          labels, values, G_N_ELEMENTS(labels),
2279                                          sp_rtb_rx_value_changed);
2280         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2281         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2282     }
2284     /* ry */
2285     {
2286         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2287         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2288         eact = create_adjustment_action( "RadiusYAction",
2289                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2290                                          "tools.shapes.rect", "ry", 0,
2291                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2292                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2293                                          labels, values, G_N_ELEMENTS(labels),
2294                                          sp_rtb_ry_value_changed);
2295         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2296         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2297     }
2299     // add the units menu
2300     {
2301         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2302         gtk_action_group_add_action( mainActions, act );
2303     }
2305     /* Reset */
2306     {
2307         InkAction* inky = ink_action_new( "RectResetAction",
2308                                           _("Not rounded"),
2309                                           _("Make corners sharp"),
2310                                           "squared_corner",
2311                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2312         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2313         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2314         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2315         g_object_set_data( holder, "not_rounded", inky );
2316     }
2318     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2319     sp_rtb_sensitivize( holder );
2321     sigc::connection *connection = new sigc::connection(
2322         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2323         );
2324     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2325     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2328 //########################
2329 //##       3D Box       ##
2330 //########################
2332 // normalize angle so that it lies in the interval [0,360]
2333 static double box3d_normalize_angle (double a) {
2334     double angle = a + ((int) (a/360.0))*360;
2335     if (angle < 0) {
2336         angle += 360.0;
2337     }
2338     return angle;
2341 static void
2342 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2343                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2344     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2345     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2346     //       are reset).
2347     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2349     if (is_infinite) {
2350         gtk_toggle_action_set_active(tact, TRUE);
2351         gtk_action_set_sensitive(act, TRUE);
2353         double angle = persp3d_get_infinite_angle(persp, axis);
2354         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2355             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2356         }
2357     } else {
2358         gtk_toggle_action_set_active(tact, FALSE);
2359         gtk_action_set_sensitive(act, FALSE);
2360     }
2363 static void
2364 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2365     if (!persp_repr) {
2366         g_print ("No perspective given to box3d_resync_toolbar().\n");
2367         return;
2368     }
2370     GtkWidget *tbl = GTK_WIDGET(data);
2371     GtkAdjustment *adj = 0;
2372     GtkAction *act = 0;
2373     GtkToggleAction *tact = 0;
2374     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2375     {
2376         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2377         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2378         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2380         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2381     }
2382     {
2383         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2384         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2385         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2387         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2388     }
2389     {
2390         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2391         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2392         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2394         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2395     }
2398 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2399                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2400                                                   bool /*is_interactive*/, gpointer data)
2402     GtkWidget *tbl = GTK_WIDGET(data);
2404     // quit if run by the attr_changed listener
2405     // note: it used to work without the differently called freeze_ attributes (here and in
2406     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2407     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2408         return;
2409     }
2411     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2412     // sp_document_maybe_done() when the document is undo insensitive)
2413     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2415     // TODO: Only update the appropriate part of the toolbar
2416 //    if (!strcmp(name, "inkscape:vp_z")) {
2417         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2418 //    }
2420     Persp3D *persp = persp3d_get_from_repr(repr);
2421     persp3d_update_box_reprs(persp);
2423     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2426 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2428     NULL, /* child_added */
2429     NULL, /* child_removed */
2430     box3d_persp_tb_event_attr_changed,
2431     NULL, /* content_changed */
2432     NULL  /* order_changed */
2433 };
2435 /**
2436  *  \param selection Should not be NULL.
2437  */
2438 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2439 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2440 static void
2441 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2443     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2444     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2445     // update the perspectives with infinite VPs and leave the other ones untouched).
2447     Inkscape::XML::Node *persp_repr = NULL;
2448     purge_repr_listener(tbl, tbl);
2450     SPItem *item = selection->singleItem();
2451     if (item && SP_IS_BOX3D(item)) {
2452         // FIXME: Also deal with multiple selected boxes
2453         SPBox3D *box = SP_BOX3D(item);
2454         Persp3D *persp = box3d_get_perspective(box);
2455         persp_repr = SP_OBJECT_REPR(persp);
2456         if (persp_repr) {
2457             g_object_set_data(tbl, "repr", persp_repr);
2458             Inkscape::GC::anchor(persp_repr);
2459             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2460             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2461         }
2463         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2464         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2465     
2466         box3d_resync_toolbar(persp_repr, tbl);
2467     }
2470 static void
2471 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2473     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2474     SPDocument *document = sp_desktop_document(desktop);
2476     // quit if run by the attr_changed listener
2477     // note: it used to work without the differently called freeze_ attributes (here and in
2478     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2479     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2480         return;
2481     }
2483     // in turn, prevent listener from responding
2484     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2486     //Persp3D *persp = document->current_persp3d;
2487     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2488     if (sel_persps.empty()) {
2489         // this can happen when the document is created; we silently ignore it
2490         return;
2491     }
2492     Persp3D *persp = *(sel_persps.begin());
2494     persp->tmat.set_infinite_direction (axis, adj->value);
2495     SP_OBJECT(persp)->updateRepr();
2497     // TODO: use the correct axis here, too
2498     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2500     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2504 static void
2505 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2507     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2510 static void
2511 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2513     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2516 static void
2517 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2519     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2523 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2525     // TODO: Take all selected perspectives into account
2526     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2527     if (sel_persps.empty()) {
2528         // this can happen when the document is created; we silently ignore it
2529         return;
2530     }
2531     Persp3D *persp = *(sel_persps.begin());
2533     bool set_infinite = gtk_toggle_action_get_active(act);
2534     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2537 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2539     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2542 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2544     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2547 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2549     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2552 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2554     EgeAdjustmentAction* eact = 0;
2555     SPDocument *document = sp_desktop_document (desktop);
2556     Persp3D *persp = document->current_persp3d;
2558     EgeAdjustmentAction* box3d_angle_x = 0;
2559     EgeAdjustmentAction* box3d_angle_y = 0;
2560     EgeAdjustmentAction* box3d_angle_z = 0;
2562     /* Angle X */
2563     {
2564         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2565         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2566         eact = create_adjustment_action( "3DBoxAngleXAction",
2567                                          _("Angle in X direction"), _("Angle X:"),
2568                                          // Translators: PL is short for 'perspective line'
2569                                          _("Angle of PLs in X direction"),
2570                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2571                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2572                                          -360.0, 360.0, 1.0, 10.0,
2573                                          labels, values, G_N_ELEMENTS(labels),
2574                                          box3d_angle_x_value_changed );
2575         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2576         g_object_set_data( holder, "box3d_angle_x_action", eact );
2577         box3d_angle_x = eact;
2578     }
2580     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2581         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2582     } else {
2583         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2584     }
2587     /* VP X state */
2588     {
2589         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2590                                                       // Translators: VP is short for 'vanishing point'
2591                                                       _("State of VP in X direction"),
2592                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2593                                                       "toggle_vp_x",
2594                                                       Inkscape::ICON_SIZE_DECORATION );
2595         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2596         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2597         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2598         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2599         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2600     }
2602     /* Angle Y */
2603     {
2604         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2605         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2606         eact = create_adjustment_action( "3DBoxAngleYAction",
2607                                          _("Angle in Y direction"), _("Angle Y:"),
2608                                          // Translators: PL is short for 'perspective line'
2609                                          _("Angle of PLs in Y direction"),
2610                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2611                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2612                                          -360.0, 360.0, 1.0, 10.0,
2613                                          labels, values, G_N_ELEMENTS(labels),
2614                                          box3d_angle_y_value_changed );
2615         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2616         g_object_set_data( holder, "box3d_angle_y_action", eact );
2617         box3d_angle_y = eact;
2618     }
2620     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2621         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2622     } else {
2623         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2624     }
2626     /* VP Y state */
2627     {
2628         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2629                                                       // Translators: VP is short for 'vanishing point'
2630                                                       _("State of VP in Y direction"),
2631                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2632                                                       "toggle_vp_y",
2633                                                       Inkscape::ICON_SIZE_DECORATION );
2634         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2635         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2636         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2637         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2638         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2639     }
2641     /* Angle Z */
2642     {
2643         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2644         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2645         eact = create_adjustment_action( "3DBoxAngleZAction",
2646                                          _("Angle in Z direction"), _("Angle Z:"),
2647                                          // Translators: PL is short for 'perspective line'
2648                                          _("Angle of PLs in Z direction"),
2649                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2650                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2651                                          -360.0, 360.0, 1.0, 10.0,
2652                                          labels, values, G_N_ELEMENTS(labels),
2653                                          box3d_angle_z_value_changed );
2654         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2655         g_object_set_data( holder, "box3d_angle_z_action", eact );
2656         box3d_angle_z = eact;
2657     }
2659     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2660         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2661     } else {
2662         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2663     }
2665     /* VP Z state */
2666     {
2667         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2668                                                       // Translators: VP is short for 'vanishing point'
2669                                                       _("State of VP in Z direction"),
2670                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2671                                                       "toggle_vp_z",
2672                                                       Inkscape::ICON_SIZE_DECORATION );
2673         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2674         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2675         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2676         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2677         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2678     }
2680     sigc::connection *connection = new sigc::connection(
2681         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2682        );
2683     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2684     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2687 //########################
2688 //##       Spiral       ##
2689 //########################
2691 static void
2692 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2694     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2696     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2697         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2698     }
2700     // quit if run by the attr_changed listener
2701     if (g_object_get_data( tbl, "freeze" )) {
2702         return;
2703     }
2705     // in turn, prevent listener from responding
2706     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2708     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2710     bool modmade = false;
2711     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2712          items != NULL;
2713          items = items->next)
2714     {
2715         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2716             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2717             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2718             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2719             modmade = true;
2720         }
2721     }
2723     g_free(namespaced_name);
2725     if (modmade) {
2726         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2727                                    _("Change spiral"));
2728     }
2730     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2733 static void
2734 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2736     sp_spl_tb_value_changed(adj, tbl, "revolution");
2739 static void
2740 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2742     sp_spl_tb_value_changed(adj, tbl, "expansion");
2745 static void
2746 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2748     sp_spl_tb_value_changed(adj, tbl, "t0");
2751 static void
2752 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2754     GtkWidget *tbl = GTK_WIDGET(obj);
2756     GtkAdjustment *adj;
2758     // fixme: make settable
2759     gdouble rev = 5;
2760     gdouble exp = 1.0;
2761     gdouble t0 = 0.0;
2763     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2764     gtk_adjustment_set_value(adj, rev);
2765     gtk_adjustment_value_changed(adj);
2767     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2768     gtk_adjustment_set_value(adj, exp);
2769     gtk_adjustment_value_changed(adj);
2771     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2772     gtk_adjustment_set_value(adj, t0);
2773     gtk_adjustment_value_changed(adj);
2775     spinbutton_defocus(GTK_OBJECT(tbl));
2779 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2780                                          gchar const */*old_value*/, gchar const */*new_value*/,
2781                                          bool /*is_interactive*/, gpointer data)
2783     GtkWidget *tbl = GTK_WIDGET(data);
2785     // quit if run by the _changed callbacks
2786     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2787         return;
2788     }
2790     // in turn, prevent callbacks from responding
2791     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2793     GtkAdjustment *adj;
2794     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2795     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2797     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2798     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2800     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2801     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2803     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2807 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2808     NULL, /* child_added */
2809     NULL, /* child_removed */
2810     spiral_tb_event_attr_changed,
2811     NULL, /* content_changed */
2812     NULL  /* order_changed */
2813 };
2815 static void
2816 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2818     int n_selected = 0;
2819     Inkscape::XML::Node *repr = NULL;
2821     purge_repr_listener( tbl, tbl );
2823     for (GSList const *items = selection->itemList();
2824          items != NULL;
2825          items = items->next)
2826     {
2827         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2828             n_selected++;
2829             repr = SP_OBJECT_REPR((SPItem *) items->data);
2830         }
2831     }
2833     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2835     if (n_selected == 0) {
2836         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2837     } else if (n_selected == 1) {
2838         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2840         if (repr) {
2841             g_object_set_data( tbl, "repr", repr );
2842             Inkscape::GC::anchor(repr);
2843             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2844             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2845         }
2846     } else {
2847         // FIXME: implement averaging of all parameters for multiple selected
2848         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2849         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2850     }
2854 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2856     EgeAdjustmentAction* eact = 0;
2858     {
2859         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2860         ege_output_action_set_use_markup( act, TRUE );
2861         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2862         g_object_set_data( holder, "mode_action", act );
2863     }
2865     /* Revolution */
2866     {
2867         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2868         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2869         eact = create_adjustment_action( "SpiralRevolutionAction",
2870                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2871                                          "tools.shapes.spiral", "revolution", 3.0,
2872                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2873                                          0.01, 1024.0, 0.1, 1.0,
2874                                          labels, values, G_N_ELEMENTS(labels),
2875                                          sp_spl_tb_revolution_value_changed, 1, 2);
2876         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2877     }
2879     /* Expansion */
2880     {
2881         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2882         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2883         eact = create_adjustment_action( "SpiralExpansionAction",
2884                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2885                                          "tools.shapes.spiral", "expansion", 1.0,
2886                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2887                                          0.0, 1000.0, 0.01, 1.0,
2888                                          labels, values, G_N_ELEMENTS(labels),
2889                                          sp_spl_tb_expansion_value_changed);
2890         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2891     }
2893     /* T0 */
2894     {
2895         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2896         gdouble values[] = {0, 0.5, 0.9};
2897         eact = create_adjustment_action( "SpiralT0Action",
2898                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2899                                          "tools.shapes.spiral", "t0", 0.0,
2900                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2901                                          0.0, 0.999, 0.01, 1.0,
2902                                          labels, values, G_N_ELEMENTS(labels),
2903                                          sp_spl_tb_t0_value_changed);
2904         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2905     }
2907     /* Reset */
2908     {
2909         InkAction* inky = ink_action_new( "SpiralResetAction",
2910                                           _("Defaults"),
2911                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2912                                           GTK_STOCK_CLEAR,
2913                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2914         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2915         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2916     }
2919     sigc::connection *connection = new sigc::connection(
2920         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2921         );
2922     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2923     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2926 //########################
2927 //##     Pen/Pencil    ##
2928 //########################
2931 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2933     // Put stuff here
2936 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2938     // Put stuff here
2941 //########################
2942 //##       Tweak        ##
2943 //########################
2945 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2947     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2950 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2952     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2955 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2957     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2960 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2962     int mode = ege_select_one_action_get_active( act );
2963     prefs_set_int_attribute("tools.tweak", "mode", mode);
2965     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2966     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2967     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2968     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2969     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2970     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2971     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2972         if (doh) gtk_action_set_sensitive (doh, TRUE);
2973         if (dos) gtk_action_set_sensitive (dos, TRUE);
2974         if (dol) gtk_action_set_sensitive (dol, TRUE);
2975         if (doo) gtk_action_set_sensitive (doo, TRUE);
2976         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2977         if (fid) gtk_action_set_sensitive (fid, FALSE);
2978     } else {
2979         if (doh) gtk_action_set_sensitive (doh, FALSE);
2980         if (dos) gtk_action_set_sensitive (dos, FALSE);
2981         if (dol) gtk_action_set_sensitive (dol, FALSE);
2982         if (doo) gtk_action_set_sensitive (doo, FALSE);
2983         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2984         if (fid) gtk_action_set_sensitive (fid, TRUE);
2985     }
2988 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2990     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2993 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2994     bool show = gtk_toggle_action_get_active( act );
2995     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2997 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2998     bool show = gtk_toggle_action_get_active( act );
2999     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3001 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3002     bool show = gtk_toggle_action_get_active( act );
3003     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3005 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3006     bool show = gtk_toggle_action_get_active( act );
3007     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3010 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3012     {
3013         /* Width */
3014         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3015         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3016         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3017                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3018                                                               "tools.tweak", "width", 15,
3019                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3020                                                               1, 100, 1.0, 10.0,
3021                                                               labels, values, G_N_ELEMENTS(labels),
3022                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3023         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3024         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3025     }
3028     {
3029         /* Force */
3030         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3031         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3032         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3033                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3034                                                               "tools.tweak", "force", 20,
3035                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3036                                                               1, 100, 1.0, 10.0,
3037                                                               labels, values, G_N_ELEMENTS(labels),
3038                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3039         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3040         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3041     }
3043     /* Mode */
3044     {
3045         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3047         GtkTreeIter iter;
3048         gtk_list_store_append( model, &iter );
3049         gtk_list_store_set( model, &iter,
3050                             0, _("Push mode"),
3051                             1, _("Push parts of paths in any direction"),
3052                             2, "tweak_push_mode",
3053                             -1 );
3055         gtk_list_store_append( model, &iter );
3056         gtk_list_store_set( model, &iter,
3057                             0, _("Shrink mode"),
3058                             1, _("Shrink (inset) parts of paths"),
3059                             2, "tweak_shrink_mode",
3060                             -1 );
3062         gtk_list_store_append( model, &iter );
3063         gtk_list_store_set( model, &iter,
3064                             0, _("Grow mode"),
3065                             1, _("Grow (outset) parts of paths"),
3066                             2, "tweak_grow_mode",
3067                             -1 );
3069         gtk_list_store_append( model, &iter );
3070         gtk_list_store_set( model, &iter,
3071                             0, _("Attract mode"),
3072                             1, _("Attract parts of paths towards cursor"),
3073                             2, "tweak_attract_mode",
3074                             -1 );
3076         gtk_list_store_append( model, &iter );
3077         gtk_list_store_set( model, &iter,
3078                             0, _("Repel mode"),
3079                             1, _("Repel parts of paths from cursor"),
3080                             2, "tweak_repel_mode",
3081                             -1 );
3083         gtk_list_store_append( model, &iter );
3084         gtk_list_store_set( model, &iter,
3085                             0, _("Roughen mode"),
3086                             1, _("Roughen parts of paths"),
3087                             2, "tweak_roughen_mode",
3088                             -1 );
3090         gtk_list_store_append( model, &iter );
3091         gtk_list_store_set( model, &iter,
3092                             0, _("Color paint mode"),
3093                             1, _("Paint the tool's color upon selected objects"),
3094                             2, "tweak_colorpaint_mode",
3095                             -1 );
3097         gtk_list_store_append( model, &iter );
3098         gtk_list_store_set( model, &iter,
3099                             0, _("Color jitter mode"),
3100                             1, _("Jitter the colors of selected objects"),
3101                             2, "tweak_colorjitter_mode",
3102                             -1 );
3104         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3105         g_object_set( act, "short_label", _("Mode:"), NULL );
3106         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3107         g_object_set_data( holder, "mode_action", act );
3109         ege_select_one_action_set_appearance( act, "full" );
3110         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3111         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3112         ege_select_one_action_set_icon_column( act, 2 );
3113         ege_select_one_action_set_tooltip_column( act, 1  );
3115         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3116         ege_select_one_action_set_active( act, mode );
3117         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3119         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3120     }
3122     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3124     {
3125         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3126         ege_output_action_set_use_markup( act, TRUE );
3127         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3128         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3129             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3130         g_object_set_data( holder, "tweak_channels_label", act);
3131     }
3133     {
3134         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3135                                                       _("Hue"),
3136                                                       _("In color mode, act on objects' hue"),
3137                                                       NULL,
3138                                                       Inkscape::ICON_SIZE_DECORATION );
3139         g_object_set( act, "short_label", _("H"), NULL );
3140         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3141         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3142         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3143         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3144             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3145         g_object_set_data( holder, "tweak_doh", act);
3146     }
3147     {
3148         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3149                                                       _("Saturation"),
3150                                                       _("In color mode, act on objects' saturation"),
3151                                                       NULL,
3152                                                       Inkscape::ICON_SIZE_DECORATION );
3153         g_object_set( act, "short_label", _("S"), NULL );
3154         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3155         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3156         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3157         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3158             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3159         g_object_set_data( holder, "tweak_dos", act );
3160     }
3161     {
3162         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3163                                                       _("Lightness"),
3164                                                       _("In color mode, act on objects' lightness"),
3165                                                       NULL,
3166                                                       Inkscape::ICON_SIZE_DECORATION );
3167         g_object_set( act, "short_label", _("L"), NULL );
3168         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3169         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3170         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3171         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3172             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3173         g_object_set_data( holder, "tweak_dol", act );
3174     }
3175     {
3176         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3177                                                       _("Opacity"),
3178                                                       _("In color mode, act on objects' opacity"),
3179                                                       NULL,
3180                                                       Inkscape::ICON_SIZE_DECORATION );
3181         g_object_set( act, "short_label", _("O"), NULL );
3182         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3183         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3184         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3185         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3186             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3187         g_object_set_data( holder, "tweak_doo", act );
3188     }
3190     {   /* Fidelity */
3191         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3192         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3193         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3194                                                               _("Fidelity"), _("Fidelity:"),
3195                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3196                                                               "tools.tweak", "fidelity", 50,
3197                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3198                                                               1, 100, 1.0, 10.0,
3199                                                               labels, values, G_N_ELEMENTS(labels),
3200                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3201         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3202         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3203         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3204             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3205         g_object_set_data( holder, "tweak_fidelity", eact );
3206     }
3209     /* Use Pressure button */
3210     {
3211         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3212                                                       _("Pressure"),
3213                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3214                                                       "use_pressure",
3215                                                       Inkscape::ICON_SIZE_DECORATION );
3216         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3217         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3218         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3219     }
3224 //########################
3225 //##     Calligraphy    ##
3226 //########################
3228 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3230     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3233 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3235     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3238 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3240     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3243 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3245     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3248 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3250     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3253 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3255     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3258 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3260     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3263 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3265     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3268 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3270     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3273 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3275     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3278 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3280     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3282     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3285 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3287     // FIXME: make defaults settable via Inkscape Options
3288     struct KeyValue {
3289         char const *key;
3290         double value;
3291     } const key_values[] = {
3292         {"mass", 0.02},
3293         {"wiggle", 0.0},
3294         {"angle", 30.0},
3295         {"width", 15},
3296         {"thinning", 0.1},
3297         {"tremor", 0.0},
3298         {"flatness", 0.9},
3299         {"cap_rounding", 0.0}
3300     };
3302     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3303         KeyValue const &kv = key_values[i];
3304         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3305         if ( adj ) {
3306             gtk_adjustment_set_value(adj, kv.value);
3307         }
3308     }
3312 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3314     {
3315         EgeAdjustmentAction* calligraphy_angle = 0;
3317         {
3318         /* Width */
3319         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3320         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3321         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3322                                                               _("Pen Width"), _("Width:"),
3323                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3324                                                               "tools.calligraphic", "width", 15,
3325                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3326                                                               1, 100, 1.0, 10.0,
3327                                                               labels, values, G_N_ELEMENTS(labels),
3328                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3329         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3330         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3331         }
3333         {
3334         /* Thinning */
3335             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3336             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3337         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3338                                                               _("Stroke Thinning"), _("Thinning:"),
3339                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3340                                                               "tools.calligraphic", "thinning", 0.1,
3341                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3342                                                               -1.0, 1.0, 0.01, 0.1,
3343                                                               labels, values, G_N_ELEMENTS(labels),
3344                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3345         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3346         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3347         }
3349         {
3350         /* Angle */
3351         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3352         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3353         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3354                                                               _("Pen Angle"), _("Angle:"),
3355                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3356                                                               "tools.calligraphic", "angle", 30,
3357                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3358                                                               -90.0, 90.0, 1.0, 10.0,
3359                                                               labels, values, G_N_ELEMENTS(labels),
3360                                                               sp_ddc_angle_value_changed, 1, 0 );
3361         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3362         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3363         calligraphy_angle = eact;
3364         }
3366         {
3367         /* Fixation */
3368             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3369         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3370         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3371                                                               _("Fixation"), _("Fixation:"),
3372                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3373                                                               "tools.calligraphic", "flatness", 0.9,
3374                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3375                                                               0.0, 1.0, 0.01, 0.1,
3376                                                               labels, values, G_N_ELEMENTS(labels),
3377                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3378         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3379         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3380         }
3382         {
3383         /* Cap Rounding */
3384             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3385         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3386         // TRANSLATORS: "cap" means "end" (both start and finish) here
3387         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3388                                                               _("Cap rounding"), _("Caps:"),
3389                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3390                                                               "tools.calligraphic", "cap_rounding", 0.0,
3391                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3392                                                               0.0, 5.0, 0.01, 0.1,
3393                                                               labels, values, G_N_ELEMENTS(labels),
3394                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3395         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3396         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3397         }
3399         {
3400         /* Tremor */
3401             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3402         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3403         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3404                                                               _("Stroke Tremor"), _("Tremor:"),
3405                                                               _("Increase to make strokes rugged and trembling"),
3406                                                               "tools.calligraphic", "tremor", 0.0,
3407                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3408                                                               0.0, 1.0, 0.01, 0.1,
3409                                                               labels, values, G_N_ELEMENTS(labels),
3410                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3412         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3413         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3414         }
3416         {
3417         /* Wiggle */
3418         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3419         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3420         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3421                                                               _("Pen Wiggle"), _("Wiggle:"),
3422                                                               _("Increase to make the pen waver and wiggle"),
3423                                                               "tools.calligraphic", "wiggle", 0.0,
3424                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3425                                                               0.0, 1.0, 0.01, 0.1,
3426                                                               labels, values, G_N_ELEMENTS(labels),
3427                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3428         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3429         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3430         }
3432         {
3433         /* Mass */
3434             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3435         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3436         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3437                                                               _("Pen Mass"), _("Mass:"),
3438                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3439                                                               "tools.calligraphic", "mass", 0.02,
3440                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3441                                                               0.0, 1.0, 0.01, 0.1,
3442                                                               labels, values, G_N_ELEMENTS(labels),
3443                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3444         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3445         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3446         }
3449         /* Trace Background button */
3450         {
3451             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3452                                                           _("Trace Background"),
3453                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3454                                                           "trace_background",
3455                                                           Inkscape::ICON_SIZE_DECORATION );
3456             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3457             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3458             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3459         }
3461         /* Use Pressure button */
3462         {
3463             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3464                                                           _("Pressure"),
3465                                                           _("Use the pressure of the input device to alter the width of the pen"),
3466                                                           "use_pressure",
3467                                                           Inkscape::ICON_SIZE_DECORATION );
3468             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3469             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3470             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3471         }
3473         /* Use Tilt button */
3474         {
3475             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3476                                                           _("Tilt"),
3477                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3478                                                           "use_tilt",
3479                                                           Inkscape::ICON_SIZE_DECORATION );
3480             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3481             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3482             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3483             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3484         }
3486         /* Reset */
3487         {
3488             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3489                                              _("Defaults"),
3490                                              _("Reset all parameters to defaults"),
3491                                              GTK_STOCK_CLEAR );
3492             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3493             gtk_action_group_add_action( mainActions, act );
3494             gtk_action_set_sensitive( act, TRUE );
3495         }
3496     }
3500 //########################
3501 //##    Circle / Arc    ##
3502 //########################
3504 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3506     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3507     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3509     if (v1 == 0 && v2 == 0) {
3510         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3511             gtk_action_set_sensitive( ocb, FALSE );
3512             gtk_action_set_sensitive( make_whole, FALSE );
3513         }
3514     } else {
3515         gtk_action_set_sensitive( ocb, TRUE );
3516         gtk_action_set_sensitive( make_whole, TRUE );
3517     }
3520 static void
3521 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3523     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3525     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3526         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3527     }
3529     // quit if run by the attr_changed listener
3530     if (g_object_get_data( tbl, "freeze" )) {
3531         return;
3532     }
3534     // in turn, prevent listener from responding
3535     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3537     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3539     bool modmade = false;
3540     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3541          items != NULL;
3542          items = items->next)
3543     {
3544         SPItem *item = SP_ITEM(items->data);
3546         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3548             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3549             SPArc *arc = SP_ARC(item);
3551             if (!strcmp(value_name, "start"))
3552                 ge->start = (adj->value * M_PI)/ 180;
3553             else
3554                 ge->end = (adj->value * M_PI)/ 180;
3556             sp_genericellipse_normalize(ge);
3557             ((SPObject *)arc)->updateRepr();
3558             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3560             modmade = true;
3561         }
3562     }
3564     g_free(namespaced_name);
3566     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3568     sp_arctb_sensitivize( tbl, adj->value, other->value );
3570     if (modmade) {
3571         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3572                                    _("Arc: Change start/end"));
3573     }
3575     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3579 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3581     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3584 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3586     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3589 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3591     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3592     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3593         if ( ege_select_one_action_get_active( act ) != 0 ) {
3594             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3595         } else {
3596             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3597         }
3598     }
3600     // quit if run by the attr_changed listener
3601     if (g_object_get_data( tbl, "freeze" )) {
3602         return;
3603     }
3605     // in turn, prevent listener from responding
3606     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3608     bool modmade = false;
3610     if ( ege_select_one_action_get_active(act) != 0 ) {
3611         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3612              items != NULL;
3613              items = items->next)
3614         {
3615             if (SP_IS_ARC((SPItem *) items->data)) {
3616                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3617                 repr->setAttribute("sodipodi:open", "true");
3618                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3619                 modmade = true;
3620             }
3621         }
3622     } else {
3623         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3624              items != NULL;
3625              items = items->next)
3626         {
3627             if (SP_IS_ARC((SPItem *) items->data))    {
3628                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3629                 repr->setAttribute("sodipodi:open", NULL);
3630                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3631                 modmade = true;
3632             }
3633         }
3634     }
3636     if (modmade) {
3637         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3638                                    _("Arc: Change open/closed"));
3639     }
3641     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3644 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3646     GtkAdjustment *adj;
3647     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3648     gtk_adjustment_set_value(adj, 0.0);
3649     gtk_adjustment_value_changed(adj);
3651     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3652     gtk_adjustment_set_value(adj, 0.0);
3653     gtk_adjustment_value_changed(adj);
3655     spinbutton_defocus( GTK_OBJECT(obj) );
3658 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3659                                       gchar const */*old_value*/, gchar const */*new_value*/,
3660                                       bool /*is_interactive*/, gpointer data)
3662     GObject *tbl = G_OBJECT(data);
3664     // quit if run by the _changed callbacks
3665     if (g_object_get_data( tbl, "freeze" )) {
3666         return;
3667     }
3669     // in turn, prevent callbacks from responding
3670     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3672     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3673     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3675     GtkAdjustment *adj1,*adj2;
3676     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3677     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3678     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3679     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3681     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3683     char const *openstr = NULL;
3684     openstr = repr->attribute("sodipodi:open");
3685     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3687     if (openstr) {
3688         ege_select_one_action_set_active( ocb, 1 );
3689     } else {
3690         ege_select_one_action_set_active( ocb, 0 );
3691     }
3693     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3696 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3697     NULL, /* child_added */
3698     NULL, /* child_removed */
3699     arc_tb_event_attr_changed,
3700     NULL, /* content_changed */
3701     NULL  /* order_changed */
3702 };
3705 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3707     int n_selected = 0;
3708     Inkscape::XML::Node *repr = NULL;
3710     purge_repr_listener( tbl, tbl );
3712     for (GSList const *items = selection->itemList();
3713          items != NULL;
3714          items = items->next)
3715     {
3716         if (SP_IS_ARC((SPItem *) items->data)) {
3717             n_selected++;
3718             repr = SP_OBJECT_REPR((SPItem *) items->data);
3719         }
3720     }
3722     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3724     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3725     if (n_selected == 0) {
3726         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3727     } else if (n_selected == 1) {
3728         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3729         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3731         if (repr) {
3732             g_object_set_data( tbl, "repr", repr );
3733             Inkscape::GC::anchor(repr);
3734             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3735             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3736         }
3737     } else {
3738         // FIXME: implement averaging of all parameters for multiple selected
3739         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3740         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3741         sp_arctb_sensitivize( tbl, 1, 0 );
3742     }
3746 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3748     EgeAdjustmentAction* eact = 0;
3751     {
3752         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3753         ege_output_action_set_use_markup( act, TRUE );
3754         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3755         g_object_set_data( holder, "mode_action", act );
3756     }
3758     /* Start */
3759     {
3760         eact = create_adjustment_action( "ArcStartAction",
3761                                          _("Start"), _("Start:"),
3762                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3763                                          "tools.shapes.arc", "start", 0.0,
3764                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3765                                          -360.0, 360.0, 1.0, 10.0,
3766                                          0, 0, 0,
3767                                          sp_arctb_start_value_changed);
3768         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3769     }
3771     /* End */
3772     {
3773         eact = create_adjustment_action( "ArcEndAction",
3774                                          _("End"), _("End:"),
3775                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3776                                          "tools.shapes.arc", "end", 0.0,
3777                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3778                                          -360.0, 360.0, 1.0, 10.0,
3779                                          0, 0, 0,
3780                                          sp_arctb_end_value_changed);
3781         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3782     }
3784     /* Segments / Pie checkbox */
3785     {
3786         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3788         GtkTreeIter iter;
3789         gtk_list_store_append( model, &iter );
3790         gtk_list_store_set( model, &iter,
3791                             0, _("Closed arc"),
3792                             1, _("Switch to segment (closed shape with two radii)"),
3793                             2, "circle_closed_arc",
3794                             -1 );
3796         gtk_list_store_append( model, &iter );
3797         gtk_list_store_set( model, &iter,
3798                             0, _("Open Arc"),
3799                             1, _("Switch to arc (unclosed shape)"),
3800                             2, "circle_open_arc",
3801                             -1 );
3803         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3804         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3805         g_object_set_data( holder, "open_action", act );
3807         ege_select_one_action_set_appearance( act, "full" );
3808         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3809         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3810         ege_select_one_action_set_icon_column( act, 2 );
3811         ege_select_one_action_set_tooltip_column( act, 1  );
3813         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3814         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3815         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3816         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3817     }
3819     /* Make Whole */
3820     {
3821         InkAction* inky = ink_action_new( "ArcResetAction",
3822                                           _("Make whole"),
3823                                           _("Make the shape a whole ellipse, not arc or segment"),
3824                                           "reset_circle",
3825                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3826         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3827         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3828         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3829         g_object_set_data( holder, "make_whole", inky );
3830     }
3832     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3833     // sensitivize make whole and open checkbox
3834     {
3835         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3836         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3837         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3838     }
3841     sigc::connection *connection = new sigc::connection(
3842         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3843         );
3844     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3845     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3851 // toggle button callbacks and updaters
3853 //########################
3854 //##      Dropper       ##
3855 //########################
3857 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3858     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3859     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3860     if ( set_action ) {
3861         if ( gtk_toggle_action_get_active( act ) ) {
3862             gtk_action_set_sensitive( set_action, TRUE );
3863         } else {
3864             gtk_action_set_sensitive( set_action, FALSE );
3865         }
3866     }
3868     spinbutton_defocus(GTK_OBJECT(tbl));
3871 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3872     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3873     spinbutton_defocus(GTK_OBJECT(tbl));
3877 /**
3878  * Dropper auxiliary toolbar construction and setup.
3879  *
3880  * TODO: Would like to add swatch of current color.
3881  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3882  *       can drag and drop places. Will provide a nice mixing palette.
3883  */
3884 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3886     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3888     {
3889         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3890                                                       _("Pick alpha"),
3891                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3892                                                       "color_alpha_get",
3893                                                       Inkscape::ICON_SIZE_DECORATION );
3894         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3895         g_object_set_data( holder, "pick_action", act );
3896         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3897         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3898     }
3900     {
3901         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3902                                                       _("Set alpha"),
3903                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3904                                                       "color_alpha_set",
3905                                                       Inkscape::ICON_SIZE_DECORATION );
3906         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3907         g_object_set_data( holder, "set_action", act );
3908         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3909         // make sure it's disabled if we're not picking alpha
3910         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3911         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3912     }
3916 //########################
3917 //##    Text Toolbox    ##
3918 //########################
3919 /*
3920 static void
3921 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3923     //Call back for letter sizing spinbutton
3926 static void
3927 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3929     //Call back for line height spinbutton
3932 static void
3933 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3935     //Call back for horizontal kerning spinbutton
3938 static void
3939 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3941     //Call back for vertical kerning spinbutton
3944 static void
3945 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3947     //Call back for letter rotation spinbutton
3948 }*/
3950 namespace {
3952 bool visible = false;
3954 void
3955 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3957     SPStyle *query =
3958         sp_style_new (SP_ACTIVE_DOCUMENT);
3959     
3960     int result_fontspec =
3961         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3963     int result_family =
3964         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3966     int result_style =
3967         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3969     int result_numbers =
3970         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3972     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3974     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3975     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3976     {
3977         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3979         if (repr)
3980         {
3981             sp_style_read_from_repr (query, repr);
3982         }
3983         else
3984         {
3985             return;
3986         }
3987     }
3989     if (query->text)
3990     {
3991         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3992             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3993             gtk_entry_set_text (GTK_ENTRY (entry), "");
3995         } else if (query->text->font_specification.value || query->text->font_family.value) {
3997             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3998             
3999             // Get the font that corresponds 
4000             Glib::ustring familyName;
4001             
4002             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4003             if (font) {
4004                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4005                 font->Unref();
4006                 font = NULL;
4007             }
4008             
4009             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4011             Gtk::TreePath path;
4012             try {
4013                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4014             } catch (...) {
4015                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4016                 return;
4017             }
4019             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4020             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4022             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4024             gtk_tree_selection_select_path (tselection, path.gobj());
4025             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4027             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4028         }
4030         //Size
4031         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4032         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4033         g_object_set_data (tbl, "size-block", gpointer(1));
4034         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4035         g_object_set_data (tbl, "size-block", gpointer(0));
4036         free (str);
4038         //Anchor
4039         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4040         {
4041             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4042             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4043             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4044             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4045         }
4046         else
4047         {
4048             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4049             {
4050                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4051                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4052                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4053                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4054             }
4055             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4056             {
4057                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4058                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4059                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4060                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4061             }
4062             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4063             {
4064                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4065                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4066                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4067                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4068             }
4069         }
4071         //Style
4072         {
4073             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4075             gboolean active = gtk_toggle_button_get_active (button);
4076             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4078             if (active != check)
4079             {
4080                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4081                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4082                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4083             }
4084         }
4086         {
4087             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4089             gboolean active = gtk_toggle_button_get_active (button);
4090             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4092             if (active != check)
4093             {
4094                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4095                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4096                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4097             }
4098         }
4100         //Orientation
4101         //locking both buttons, changing one affect all group (both)
4102         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4103         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4105         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4106         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4108         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4109         {
4110             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4111         }
4112         else
4113         {
4114             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4115         }
4116         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4117         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4118     }
4120     sp_style_unref(query);
4123 void
4124 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4126     sp_text_toolbox_selection_changed (selection, tbl);
4129 void
4130 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4132     sp_text_toolbox_selection_changed (NULL, tbl);
4135 void
4136 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4137                                 GObject             *tbl)
4139     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4140     GtkTreeModel *model = 0;
4141     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4142     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4143     GtkTreeIter   iter;
4144     char         *family = 0;
4146     (void)popdown;
4148     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4149     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4151     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4152         return;
4153     }
4155     gtk_tree_model_get (model, &iter, 0, &family, -1);
4157     if (g_object_get_data (G_OBJECT (selection), "block"))
4158     {
4159         gtk_entry_set_text (GTK_ENTRY (entry), family);
4160         return;
4161     }
4163     gtk_entry_set_text (GTK_ENTRY (entry), family);
4165     SPStyle *query =
4166         sp_style_new (SP_ACTIVE_DOCUMENT);
4168     int result_fontspec =
4169         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4170     
4171     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4172     
4173     SPCSSAttr *css = sp_repr_css_attr_new ();
4174     
4175     
4176     // First try to get the font spec from the stored value
4177     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4178     
4179     if (fontSpec.empty()) {
4180         // Construct a new font specification if it does not yet exist
4181         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4182         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4183         fontFromStyle->Unref();
4184     }
4185     
4186     if (!fontSpec.empty()) {
4187         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4188         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4189             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4190             if (font) {
4191                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4192                 
4193                 // Set all the these just in case they were altered when finding the best
4194                 // match for the new family and old style...
4195                 
4196                 gchar c[256];
4197                 
4198                 font->Family(c, 256);
4199                 sp_repr_css_set_property (css, "font-family", c);
4200                 
4201                 font->Attribute( "weight", c, 256);
4202                 sp_repr_css_set_property (css, "font-weight", c);
4203                 
4204                 font->Attribute("style", c, 256);
4205                 sp_repr_css_set_property (css, "font-style", c);
4206                 
4207                 font->Attribute("stretch", c, 256);
4208                 sp_repr_css_set_property (css, "font-stretch", c);
4209                 
4210                 font->Attribute("variant", c, 256);
4211                 sp_repr_css_set_property (css, "font-variant", c);
4212                 
4213                 font->Unref();
4214             }
4215         }
4216     }
4218     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4219     if (result_fontspec == QUERY_STYLE_NOTHING)
4220     {
4221         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4222         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4223     }
4224     else
4225     {
4226         sp_desktop_set_style (desktop, css, true, true);
4227     }
4229     sp_style_unref(query);
4231     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4232                                    _("Text: Change font family"));
4233     sp_repr_css_attr_unref (css);
4234     free (family);
4235     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4237     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4240 void
4241 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4242                                        GObject      *tbl)
4244     const char *family = gtk_entry_get_text (entry);
4246     try {
4247         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4248         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4249         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4250         gtk_tree_selection_select_path (selection, path.gobj());
4251         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4252         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4253     } catch (...) {
4254         if (family && strlen (family))
4255         {
4256             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4257         }
4258     }
4261 void
4262 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4263                                    gpointer          data)
4265     if (g_object_get_data (G_OBJECT (button), "block")) return;
4266     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4267     int prop = GPOINTER_TO_INT(data);
4269     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4270     SPCSSAttr *css = sp_repr_css_attr_new ();
4272     switch (prop)
4273     {
4274         case 0:
4275         {
4276             sp_repr_css_set_property (css, "text-anchor", "start");
4277             sp_repr_css_set_property (css, "text-align", "start");
4278             break;
4279         }
4280         case 1:
4281         {
4282             sp_repr_css_set_property (css, "text-anchor", "middle");
4283             sp_repr_css_set_property (css, "text-align", "center");
4284             break;
4285         }
4287         case 2:
4288         {
4289             sp_repr_css_set_property (css, "text-anchor", "end");
4290             sp_repr_css_set_property (css, "text-align", "end");
4291             break;
4292         }
4294         case 3:
4295         {
4296             sp_repr_css_set_property (css, "text-anchor", "start");
4297             sp_repr_css_set_property (css, "text-align", "justify");
4298             break;
4299         }
4300     }
4302     SPStyle *query =
4303         sp_style_new (SP_ACTIVE_DOCUMENT);
4304     int result_numbers =
4305         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4307     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4308     if (result_numbers == QUERY_STYLE_NOTHING)
4309     {
4310         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4311     }
4313     sp_style_unref(query);
4315     sp_desktop_set_style (desktop, css, true, true);
4316     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4317                                    _("Text: Change alignment"));
4318     sp_repr_css_attr_unref (css);
4320     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4323 void
4324 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4325                                gpointer          data)
4327     if (g_object_get_data (G_OBJECT (button), "block")) return;
4329     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4330     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4331     int          prop       = GPOINTER_TO_INT(data);
4332     bool         active     = gtk_toggle_button_get_active (button);
4334     SPStyle *query =
4335         sp_style_new (SP_ACTIVE_DOCUMENT);
4336     
4337     int result_fontspec =
4338         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4340     int result_family =
4341         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4343     int result_style =
4344         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4346     int result_numbers =
4347         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4348     
4349     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4350     Glib::ustring newFontSpec = "";
4351     
4352     if (fontSpec.empty()) {
4353         // Construct a new font specification if it does not yet exist
4354         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4355         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4356         fontFromStyle->Unref();
4357     }
4358     
4359     switch (prop)
4360     {
4361         case 0:
4362         {
4363             if (!fontSpec.empty()) {
4364                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4365             }
4366             if (fontSpec != newFontSpec) {
4367                 // Don't even set the bold if the font didn't exist on the system
4368                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4369             }
4370             break;
4371         }
4373         case 1:
4374         {
4375             if (!fontSpec.empty()) {
4376                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4377             }
4378             if (fontSpec != newFontSpec) {
4379                 // Don't even set the italic if the font didn't exist on the system
4380                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4381             }
4382             break;
4383         }
4384     }
4386     if (!newFontSpec.empty()) {
4387         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str()); 
4388     }
4390     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4391     if (result_fontspec == QUERY_STYLE_NOTHING)
4392     {
4393         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4394     }
4396     sp_style_unref(query);
4398     sp_desktop_set_style (desktop, css, true, true);
4399     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4400                                    _("Text: Change font style"));
4401     sp_repr_css_attr_unref (css);
4403     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4406 void
4407 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4408                                      gpointer         data)
4410     if (g_object_get_data (G_OBJECT (button), "block")) {
4411         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4412         return;
4413     }
4415     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4416     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4417     int          prop       = GPOINTER_TO_INT(data);
4419     switch (prop)
4420     {
4421         case 0:
4422         {
4423             sp_repr_css_set_property (css, "writing-mode", "lr");
4424             break;
4425         }
4427         case 1:
4428         {
4429             sp_repr_css_set_property (css, "writing-mode", "tb");
4430             break;
4431         }
4432     }
4434     SPStyle *query =
4435         sp_style_new (SP_ACTIVE_DOCUMENT);
4436     int result_numbers =
4437         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4439     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4440     if (result_numbers == QUERY_STYLE_NOTHING)
4441     {
4442         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4443     }
4445     sp_desktop_set_style (desktop, css, true, true);
4446     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4447                                    _("Text: Change orientation"));
4448     sp_repr_css_attr_unref (css);
4450     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4453 gboolean
4454 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4456     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4457     if (!desktop) return FALSE;
4459     switch (get_group0_keyval (event)) {
4460         case GDK_Escape: // defocus
4461             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4462             return TRUE; // I consumed the event
4463             break;
4464         case GDK_Return: // defocus
4465         case GDK_KP_Enter:
4466             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4467             return TRUE; // I consumed the event
4468             break;
4469     }
4470     return FALSE;
4473 gboolean
4474 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4476     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4477     if (!desktop) return FALSE;
4479     switch (get_group0_keyval (event)) {
4480         case GDK_Escape: // defocus
4481             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4482             sp_text_toolbox_selection_changed (NULL, tbl); // update
4483             return TRUE; // I consumed the event
4484             break;
4485     }
4486     return FALSE;
4489 gboolean
4490 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4492     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4493     if (!desktop) return FALSE;
4495     switch (get_group0_keyval (event)) {
4496         case GDK_KP_Enter:
4497         case GDK_Return:
4498         case GDK_Escape: // defocus
4499             gtk_widget_hide (w);
4500             visible = false;
4501             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4502             return TRUE; // I consumed the event
4503             break;
4504     }
4505     return FALSE;
4509 void
4510 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4511                                GObject     *tbl)
4513     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4515     if (g_object_get_data (tbl, "size-block")) return;
4517     char *text = gtk_combo_box_get_active_text (cbox);
4519     SPCSSAttr *css = sp_repr_css_attr_new ();
4520     sp_repr_css_set_property (css, "font-size", text);
4521     free (text);
4523     SPStyle *query =
4524         sp_style_new (SP_ACTIVE_DOCUMENT);
4525     int result_numbers =
4526         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4528     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4529     if (result_numbers == QUERY_STYLE_NOTHING)
4530     {
4531         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4532     }
4534     sp_style_unref(query);
4536     sp_desktop_set_style (desktop, css, true, true);
4537     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4538                                    _("Text: Change font size"));
4539     sp_repr_css_attr_unref (css);
4542     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4543         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4546 void
4547 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4548                                          GObject            *tbl)
4550     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4551     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4552     int x, y;
4554     if (!visible)
4555     {
4556         gdk_window_get_origin (widget->window, &x, &y);
4557         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4558         gtk_widget_show_all (popdown);
4560         gdk_pointer_grab (widget->window, TRUE,
4561                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4562                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4563                                         GDK_POINTER_MOTION_MASK),
4564                           NULL, NULL, GDK_CURRENT_TIME);
4566         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4568         visible = true;
4569     }
4570     else
4571     {
4572         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4573         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4574         gtk_widget_hide (popdown);
4575         visible = false;
4576     }
4579 gboolean
4580 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4581                                  GdkEventFocus    */*event*/,
4582                                  GObject          */*tbl*/)
4584     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4585     return FALSE;
4588 gboolean
4589 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4590                                    GdkEventFocus    */*event*/,
4591                                    GObject          */*tbl*/)
4593     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4595     gtk_widget_hide (popdown);
4596     visible = false;
4597     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4598     return TRUE;
4601 void
4602 cell_data_func  (GtkTreeViewColumn */*column*/,
4603                  GtkCellRenderer   *cell,
4604                  GtkTreeModel      *tree_model,
4605                  GtkTreeIter       *iter,
4606                  gpointer           /*data*/)
4608     char        *family,
4609         *family_escaped,
4610         *sample_escaped;
4612     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4614     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4616     family_escaped = g_markup_escape_text (family, -1);
4617     sample_escaped = g_markup_escape_text (sample, -1);
4619     std::stringstream markup;
4620     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4621     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4623     free (family);
4624     free (family_escaped);
4625     free (sample_escaped);
4628 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4629     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4630     if (completion) {
4631         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4632         g_object_unref (completion);
4633     }
4636 GtkWidget*
4637 sp_text_toolbox_new (SPDesktop *desktop)
4639     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4641     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4642     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4644     GtkTooltips *tt = gtk_tooltips_new();
4645     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4647     ////////////Family
4648     //Window
4649     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4650     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4652     //Entry
4653     GtkWidget           *entry = gtk_entry_new ();
4654     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4655     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4656     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4657     gtk_entry_completion_set_text_column (completion, 0);
4658     gtk_entry_completion_set_minimum_key_length (completion, 1);
4659     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4660     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4661     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4662     aux_toolbox_space (tbl, 1);
4663     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4664     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4666     //Button
4667     GtkWidget   *button = gtk_button_new ();
4668     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4669     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4671     //Popdown
4672     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4673     GtkWidget           *treeview = gtk_tree_view_new ();
4675     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4676     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4677     gtk_tree_view_column_pack_start (column, cell, FALSE);
4678     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4679     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4680     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4682     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4683     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4684     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4686     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4688     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4689     gtk_container_add (GTK_CONTAINER (sw), treeview);
4691     gtk_container_add (GTK_CONTAINER (window), sw);
4692     gtk_widget_set_size_request (window, 300, 450);
4694     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4695     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4696     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4698     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4700     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4701     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4703     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4704     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4706     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4707     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4708     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4709     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4710     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4712     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4713     aux_toolbox_space (tbl, 1);
4714     GtkWidget *box = gtk_event_box_new ();
4715     gtk_container_add (GTK_CONTAINER (box), image);
4716     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4717     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4718     GtkTooltips *tooltips = gtk_tooltips_new ();
4719     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4720     gtk_widget_hide (GTK_WIDGET (box));
4721     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4723     ////////////Size
4724     const char *sizes[] = {
4725         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4726         "16", "18", "20", "22", "24", "28",
4727         "32", "36", "40", "48", "56", "64", "72", "144"
4728     };
4730     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4731     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4732     gtk_widget_set_size_request (cbox, 80, -1);
4733     aux_toolbox_space (tbl, 1);
4734     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4735     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4736     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4737     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4739     //spacer
4740     aux_toolbox_space (tbl, 4);
4741     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4743     ////////////Text anchor
4744     GtkWidget *group   = gtk_radio_button_new (NULL);
4745     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4746     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4748     // left
4749     GtkWidget *rbutton = group;
4750     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4751     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4752     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4754     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4755     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4756     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4757     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4759     // center
4760     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4761     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4762     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4763     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4765     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4766     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4767     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4768     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4770     // right
4771     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4772     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4773     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4774     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4776     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4777     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4778     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4779     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4781     // fill
4782     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4783     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4784     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4785     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4787     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4788     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4789     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4790     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4792     aux_toolbox_space (tbl, 1);
4793     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4795     //spacer
4796     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4798     ////////////Text style
4799     row = gtk_hbox_new (FALSE, 4);
4801     // bold
4802     rbutton = gtk_toggle_button_new ();
4803     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4804     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4805     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4806     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4808     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4809     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4810     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4812     // italic
4813     rbutton = gtk_toggle_button_new ();
4814     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4815     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4816     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4817     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4819     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4820     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4821     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4823     aux_toolbox_space (tbl, 1);
4824     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4826     //spacer
4827     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4829     ////////////Text orientation
4830     group   = gtk_radio_button_new (NULL);
4831     row     = gtk_hbox_new (FALSE, 4);
4832     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4834     // horizontal
4835     rbutton = group;
4836     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4837     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4838     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4839     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4841     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4842     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4843     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4845     // vertical
4846     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4847     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4848     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4849     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4850     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4852     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4853     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4854     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4855     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4858     //watch selection
4859     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4861     sigc::connection *c_selection_changed =
4862         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4863                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4864     pool->add_connection ("selection-changed", c_selection_changed);
4866     sigc::connection *c_selection_modified =
4867         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4868                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4869     pool->add_connection ("selection-modified", c_selection_modified);
4871     sigc::connection *c_subselection_changed =
4872         new sigc::connection (desktop->connectToolSubselectionChanged
4873                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4874     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4876     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4879     gtk_widget_show_all (tbl);
4880     return tbl;
4882 } // end of sp_text_toolbox_new()
4884 }//<unnamed> namespace
4887 //#########################
4888 //##      Connector      ##
4889 //#########################
4891 static void sp_connector_path_set_avoid(void)
4893     cc_selection_set_avoid(true);
4897 static void sp_connector_path_set_ignore(void)
4899     cc_selection_set_avoid(false);
4904 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4906     // quit if run by the _changed callbacks
4907     if (g_object_get_data( tbl, "freeze" )) {
4908         return;
4909     }
4911     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4912     SPDocument *doc = sp_desktop_document(desktop);
4914     if (!sp_document_get_undo_sensitive(doc))
4915     {
4916         return;
4917     }
4919     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4921     if ( repr->attribute("inkscape:connector-spacing") ) {
4922         gdouble priorValue = gtk_adjustment_get_value(adj);
4923         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4924         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4925             return;
4926         }
4927     } else if ( adj->value == defaultConnSpacing ) {
4928         return;
4929     }
4931     // in turn, prevent callbacks from responding
4932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4934     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4935     SP_OBJECT(desktop->namedview)->updateRepr();
4937     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4938     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4939         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4940         NR::Matrix m = NR::identity();
4941         avoid_item_move(&m, item);
4942     }
4944     if (items) {
4945         g_slist_free(items);
4946     }
4948     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4949             _("Change connector spacing"));
4951     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4953     spinbutton_defocus(GTK_OBJECT(tbl));
4956 static void sp_connector_graph_layout(void)
4958     if (!SP_ACTIVE_DESKTOP) return;
4960     // hack for clones, see comment in align-and-distribute.cpp
4961     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4962     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4964     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4966     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4968     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4971 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4973     if ( gtk_toggle_action_get_active( act ) ) {
4974         prefs_set_string_attribute("tools.connector", "directedlayout",
4975                 "true");
4976     } else {
4977         prefs_set_string_attribute("tools.connector", "directedlayout",
4978                 "false");
4979     }
4982 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4984     if ( gtk_toggle_action_get_active( act ) ) {
4985         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4986                 "true");
4987     } else {
4988         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4989                 "false");
4990     }
4994 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4996     prefs_set_double_attribute("tools.connector", "length", adj->value);
4997     spinbutton_defocus(GTK_OBJECT(tbl));
5000 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5001                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5002                                             bool /*is_interactive*/, gpointer data)
5004     GtkWidget *tbl = GTK_WIDGET(data);
5006     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5007         return;
5008     }
5009     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5010         return;
5011     }
5013     GtkAdjustment *adj = (GtkAdjustment*)
5014             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5015     gdouble spacing = defaultConnSpacing;
5016     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5018     gtk_adjustment_set_value(adj, spacing);
5022 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5023     NULL, /* child_added */
5024     NULL, /* child_removed */
5025     connector_tb_event_attr_changed,
5026     NULL, /* content_changed */
5027     NULL  /* order_changed */
5028 };
5031 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5033     {
5034         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5035                                           _("Avoid"),
5036                                           _("Make connectors avoid selected objects"),
5037                                           "connector_avoid",
5038                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5039         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5040         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5041     }
5043     {
5044         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5045                                           _("Ignore"),
5046                                           _("Make connectors ignore selected objects"),
5047                                           "connector_ignore",
5048                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5049         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5050         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5051     }
5053     EgeAdjustmentAction* eact = 0;
5055     // Spacing spinbox
5056     eact = create_adjustment_action( "ConnectorSpacingAction",
5057                                      _("Connector Spacing"), _("Spacing:"),
5058                                      _("The amount of space left around objects by auto-routing connectors"),
5059                                      "tools.connector", "spacing", defaultConnSpacing,
5060                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5061                                      0, 100, 1.0, 10.0,
5062                                      0, 0, 0,
5063                                      connector_spacing_changed, 1, 0 );
5064     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5066     // Graph (connector network) layout
5067     {
5068         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5069                                           _("Graph"),
5070                                           _("Nicely arrange selected connector network"),
5071                                           "graph_layout",
5072                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5073         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5074         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5075     }
5077     // Default connector length spinbox
5078     eact = create_adjustment_action( "ConnectorLengthAction",
5079                                      _("Connector Length"), _("Length:"),
5080                                      _("Ideal length for connectors when layout is applied"),
5081                                      "tools.connector", "length", 100,
5082                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5083                                      10, 1000, 10.0, 100.0,
5084                                      0, 0, 0,
5085                                      connector_length_changed, 1, 0 );
5086     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5089     // Directed edges toggle button
5090     {
5091         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5092                                                       _("Downwards"),
5093                                                       _("Make connectors with end-markers (arrows) point downwards"),
5094                                                       "directed_graph",
5095                                                       Inkscape::ICON_SIZE_DECORATION );
5096         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5098         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5099         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5100                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5102         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5103     }
5105     // Avoid overlaps toggle button
5106     {
5107         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5108                                                       _("Remove overlaps"),
5109                                                       _("Do not allow overlapping shapes"),
5110                                                       "remove_overlaps",
5111                                                       Inkscape::ICON_SIZE_DECORATION );
5112         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5114         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5115         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5116                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5118         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5119     }
5121     // Code to watch for changes to the connector-spacing attribute in
5122     // the XML.
5123     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5124     g_assert(repr != NULL);
5126     purge_repr_listener( holder, holder );
5128     if (repr) {
5129         g_object_set_data( holder, "repr", repr );
5130         Inkscape::GC::anchor(repr);
5131         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5132         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5133     }
5134 } // end of sp_connector_toolbox_prep()
5137 //#########################
5138 //##     Paintbucket     ##
5139 //#########################
5141 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5143     gint channels = ege_select_one_action_get_active( act );
5144     flood_channels_set_channels( channels );
5147 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5149     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5152 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5154     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5157 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5159     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5160     SPUnit const *unit = tracker->getActiveUnit();
5162     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5164     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5167 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5169     // FIXME: make defaults settable via Inkscape Options
5170     struct KeyValue {
5171         char const *key;
5172         double value;
5173     } const key_values[] = {
5174         {"threshold", 15},
5175         {"offset", 0.0}
5176     };
5178     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5179         KeyValue const &kv = key_values[i];
5180         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5181         if ( adj ) {
5182             gtk_adjustment_set_value(adj, kv.value);
5183         }
5184     }
5186     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5187     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5188     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5189     ege_select_one_action_set_active( autogap_action, 0 );
5192 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5194     EgeAdjustmentAction* eact = 0;
5196     {
5197         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5199         GList* items = 0;
5200         gint count = 0;
5201         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5202         {
5203             GtkTreeIter iter;
5204             gtk_list_store_append( model, &iter );
5205             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5206             count++;
5207         }
5208         g_list_free( items );
5209         items = 0;
5210         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5211         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5212         ege_select_one_action_set_appearance( act1, "compact" );
5213         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5214         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5215         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5216         g_object_set_data( holder, "channels_action", act1 );
5217     }
5219     // Spacing spinbox
5220     {
5221         eact = create_adjustment_action(
5222             "ThresholdAction",
5223             _("Fill Threshold"), _("Threshold:"),
5224             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5225             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5226             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5227             0, 0, 0,
5228             paintbucket_threshold_changed, 1, 0 );
5230         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5231     }
5233     // Create the units menu.
5234     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5235     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5236     g_object_set_data( holder, "tracker", tracker );
5237     {
5238         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5239         gtk_action_group_add_action( mainActions, act );
5240     }
5242     // Offset spinbox
5243     {
5244         eact = create_adjustment_action(
5245             "OffsetAction",
5246             _("Grow/shrink by"), _("Grow/shrink by:"),
5247             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5248             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5249             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5250             0, 0, 0,
5251             paintbucket_offset_changed, 1, 2);
5252         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5254         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5255     }
5257     /* Auto Gap */
5258     {
5259         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5261         GList* items = 0;
5262         gint count = 0;
5263         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5264         {
5265             GtkTreeIter iter;
5266             gtk_list_store_append( model, &iter );
5267             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5268             count++;
5269         }
5270         g_list_free( items );
5271         items = 0;
5272         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5273         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5274         ege_select_one_action_set_appearance( act2, "compact" );
5275         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5276         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5277         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5278         g_object_set_data( holder, "autogap_action", act2 );
5279     }
5281     /* Reset */
5282     {
5283         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5284                                           _("Defaults"),
5285                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5286                                           GTK_STOCK_CLEAR );
5287         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5288         gtk_action_group_add_action( mainActions, act );
5289         gtk_action_set_sensitive( act, TRUE );
5290     }
5294 /*
5295   Local Variables:
5296   mode:c++
5297   c-file-style:"stroustrup"
5298   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5299   indent-tabs-mode:nil
5300   fill-column:99
5301   End:
5302 */
5303 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :