Code

7f4f637b4cae187628fe5eceea810ed912453d75
[inkscape.git] / src / widgets / toolbox.cpp
1 #define __SP_MAINTOOLBOX_C__
3 /** \file
4  * Controls bars for some of Inkscape's tools
5  * (for some tools, they are in their own files)
6  */
8 /*
9 *
10 * Authors:
11 *   MenTaLguY <mental@rydia.net>
12 *   Lauris Kaplinski <lauris@kaplinski.com>
13 *   bulia byak <buliabyak@users.sf.net>
14 *   Frank Felfe <innerspace@iname.com>
15 *   John Cliff <simarilius@yahoo.com>
16 *   David Turner <novalis@gnu.org>
17 *   Josh Andler <scislac@scislac.com>
18 *   Jon A. Cruz <jon@joncruz.org>
19 *
20 * Copyright (C) 2004 David Turner
21 * Copyright (C) 2003 MenTaLguY
22 * Copyright (C) 1999-2006 authors
23 * Copyright (C) 2001-2002 Ximian, Inc.
24 *
25 * Released under GNU GPL, read the file 'COPYING' for more information
26 */
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <gtkmm.h>
33 #include <gtk/gtk.h>
34 #include <iostream>
35 #include <sstream>
37 #include "widgets/button.h"
38 #include "widgets/widget-sizes.h"
39 #include "widgets/spw-utilities.h"
40 #include "widgets/spinbutton-events.h"
41 #include "dialogs/text-edit.h"
43 #include "ui/widget/style-swatch.h"
45 #include "prefs-utils.h"
46 #include "verbs.h"
47 #include "sp-namedview.h"
48 #include "desktop.h"
49 #include "desktop-handles.h"
50 #include "xml/repr.h"
51 #include "xml/node-event-vector.h"
52 #include <glibmm/i18n.h>
53 #include "helper/unit-menu.h"
54 #include "helper/units.h"
56 #include "inkscape.h"
57 #include "conn-avoid-ref.h"
60 #include "select-toolbar.h"
61 #include "gradient-toolbar.h"
63 #include "connector-context.h"
64 #include "node-context.h"
65 #include "shape-editor.h"
66 #include "sp-rect.h"
67 #include "box3d.h"
68 #include "box3d-context.h"
69 #include "sp-star.h"
70 #include "sp-spiral.h"
71 #include "sp-ellipse.h"
72 #include "sp-text.h"
73 #include "sp-flowtext.h"
74 #include "style.h"
75 #include "selection.h"
76 #include "document-private.h"
77 #include "desktop-style.h"
78 #include "../libnrtype/font-lister.h"
79 #include "../connection-pool.h"
80 #include "../prefs-utils.h"
81 #include "../inkscape-stock.h"
82 #include "icon.h"
83 #include "graphlayout/graphlayout.h"
85 #include "mod360.h"
87 #include "toolbox.h"
89 #include "flood-context.h"
91 #include "ink-action.h"
92 #include "ege-adjustment-action.h"
93 #include "ege-output-action.h"
94 #include "ege-select-one-action.h"
95 #include "helper/unit-tracker.h"
97 // FIXME: The next two lines are only temporarily added until
98 //        the final resizing behaviour of 3D boxes is sorted out.
99 #include "knotholder.h"
100 SPKnotHolder *sp_3dbox_knot_holder(SPItem *item, SPDesktop *desktop, guint number_of_handles);
102 using Inkscape::UnitTracker;
104 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
105 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
107 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
120 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
126 static struct {
127     gchar const *type_name;
128     gchar const *data_name;
129     sp_verb_t verb;
130     sp_verb_t doubleclick_verb;
131 } const tools[] = {
132     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
133     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
134     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
135     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
136     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
137 //    { "SP3DBoxContext",    "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
138     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
139     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
140     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
141     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
142     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
143     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
144     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
145     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
146     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
147     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
148     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
149     { NULL, NULL, 0, 0 }
150 };
152 static struct {
153     gchar const *type_name;
154     gchar const *data_name;
155     GtkWidget *(*create_func)(SPDesktop *desktop);
156     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
157     gchar const *ui_name;
158     gint swatch_verb_id;
159     gchar const *swatch_tool;
160     gchar const *swatch_tip;
161 } const aux_toolboxes[] = {
162     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
163       SP_VERB_INVALID, 0, 0},
164     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
165       SP_VERB_INVALID, 0, 0},
166     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
167       SP_VERB_INVALID, 0, 0},
168     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
169       SP_VERB_INVALID, 0, 0},
170     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
171       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
172     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
173       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
174     { "SP3DBoxContext",  "3dbox_toolbox",  0, sp_3dbox_toolbox_prep,             "3DBoxToolbar",
175       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
176     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
177       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
178     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
179       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
180     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
181       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
182     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
183       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
184     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
185       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
186     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
187       SP_VERB_INVALID, 0, 0},
188     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
189       SP_VERB_INVALID, 0, 0},
190     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
191       SP_VERB_INVALID, 0, 0},
192     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
193       SP_VERB_INVALID, 0, 0},
194     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
195       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
196     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
197 };
200 static gchar const * ui_descr =
201         "<ui>"
202         "  <toolbar name='SelectToolbar'>"
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_stroke' />"
221         "    <toolitem action='transform_corners' />"
222         "    <toolitem action='transform_gradient' />"
223         "    <toolitem action='transform_pattern' />"
224         "  </toolbar>"
226         "  <toolbar name='NodeToolbar'>"
227         "    <toolitem action='NodeInsertAction' />"
228         "    <toolitem action='NodeDeleteAction' />"
229         "    <separator />"
230         "    <toolitem action='NodeJoinAction' />"
231         "    <toolitem action='NodeJoinSegmentAction' />"
232         "    <toolitem action='NodeDeleteSegmentAction' />"
233         "    <toolitem action='NodeBreakAction' />"
234         "    <separator />"
235         "    <toolitem action='NodeCuspAction' />"
236         "    <toolitem action='NodeSmoothAction' />"
237         "    <toolitem action='NodeSymmetricAction' />"
238         "    <separator />"
239         "    <toolitem action='NodeLineAction' />"
240         "    <toolitem action='NodeCurveAction' />"
241         "    <separator />"
242         "    <toolitem action='ObjectToPath' />"
243         "    <toolitem action='StrokeToPath' />"
244         "    <separator />"
245         "    <toolitem action='NodesShowHandlesAction' />"
246         "  </toolbar>"
248         "  <toolbar name='TweakToolbar'>"
249         "    <toolitem action='TweakWidthAction' />"
250         "    <toolitem action='TweakForceAction' />"
251         "    <separator />"
252         "    <toolitem action='TweakModeAction' />"
253         "    <separator />"
254         "    <toolitem action='TweakFidelityAction' />"
255         "    <toolitem action='TweakPressureAction' />"
256         "  </toolbar>"
258         "  <toolbar name='ZoomToolbar'>"
259         "    <toolitem action='ZoomIn' />"
260         "    <toolitem action='ZoomOut' />"
261         "    <separator />"
262         "    <toolitem action='ZoomSelection' />"
263         "    <toolitem action='ZoomDrawing' />"
264         "    <toolitem action='ZoomPage' />"
265         "    <toolitem action='ZoomPageWidth' />"
266         "    <separator />"
267         "    <toolitem action='ZoomPrev' />"
268         "    <toolitem action='ZoomNext' />"
269         "    <separator />"
270         "    <toolitem action='Zoom1:0' />"
271         "    <toolitem action='Zoom1:2' />"
272         "    <toolitem action='Zoom2:1' />"
273         "  </toolbar>"
275         "  <toolbar name='StarToolbar'>"
276         "    <separator />"
277         "    <toolitem action='StarStateAction' />"
278         "    <separator />"
279         "    <toolitem action='FlatAction' />"
280         "    <separator />"
281         "    <toolitem action='MagnitudeAction' />"
282         "    <toolitem action='SpokeAction' />"
283         "    <toolitem action='RoundednessAction' />"
284         "    <toolitem action='RandomizationAction' />"
285         "    <separator />"
286         "    <toolitem action='StarResetAction' />"
287         "  </toolbar>"
289         "  <toolbar name='RectToolbar'>"
290         "    <toolitem action='RectStateAction' />"
291         "    <toolitem action='RectWidthAction' />"
292         "    <toolitem action='RectHeightAction' />"
293         "    <toolitem action='RadiusXAction' />"
294         "    <toolitem action='RadiusYAction' />"
295         "    <toolitem action='RectUnitsAction' />"
296         "    <separator />"
297         "    <toolitem action='RectResetAction' />"
298         "  </toolbar>"
300         "  <toolbar name='3DBoxToolbar'>"
301         "    <toolitem action='3DBoxVPXAction' />"
302         "    <toolitem action='3DBoxVPYAction' />"
303         "    <toolitem action='3DBoxVPZAction' />"
304         "    <separator />"
305         "    <toolitem action='3DBoxHandlesAction' />"
306         "    <separator />"
307         "  </toolbar>"
309         "  <toolbar name='SpiralToolbar'>"
310         "    <toolitem action='SpiralStateAction' />"
311         "    <toolitem action='SpiralRevolutionAction' />"
312         "    <toolitem action='SpiralExpansionAction' />"
313         "    <toolitem action='SpiralT0Action' />"
314         "    <separator />"
315         "    <toolitem action='SpiralResetAction' />"
316         "  </toolbar>"
318         "  <toolbar name='PenToolbar'>"
319         "  </toolbar>"
321         "  <toolbar name='PencilToolbar'>"
322         "  </toolbar>"
324         "  <toolbar name='CalligraphyToolbar'>"
325         "    <separator />"
326         "    <toolitem action='CalligraphyWidthAction' />"
327         "    <toolitem action='ThinningAction' />"
328         "    <separator />"
329         "    <toolitem action='AngleAction' />"
330         "    <toolitem action='FixationAction' />"
331         "    <toolitem action='CapRoundingAction' />"
332         "    <separator />"
333         "    <toolitem action='TremorAction' />"
334         "    <toolitem action='WiggleAction' />"
335         "    <toolitem action='MassAction' />"
336         "    <separator />"
337         "    <toolitem action='TraceAction' />"
338         "    <toolitem action='PressureAction' />"
339         "    <toolitem action='TiltAction' />"
340         "    <toolitem action='CalligraphyResetAction' />"
341         "  </toolbar>"
343         "  <toolbar name='ArcToolbar'>"
344         "    <toolitem action='ArcStateAction' />"
345         "    <separator />"
346         "    <toolitem action='ArcStartAction' />"
347         "    <toolitem action='ArcEndAction' />"
348         "    <separator />"
349         "    <toolitem action='ArcOpenAction' />"
350         "    <separator />"
351         "    <toolitem action='ArcResetAction' />"
352         "    <separator />"
353         "  </toolbar>"
355         "  <toolbar name='PaintbucketToolbar'>"
356         "    <toolitem action='ChannelsAction' />"
357         "    <separator />"
358         "    <toolitem action='ThresholdAction' />"
359         "    <separator />"
360         "    <toolitem action='OffsetAction' />"
361         "    <toolitem action='PaintbucketUnitsAction' />"
362         "    <separator />"
363         "    <toolitem action='AutoGapAction' />"
364         "    <separator />"
365         "    <toolitem action='PaintbucketResetAction' />"
366         "  </toolbar>"
368         "  <toolbar name='DropperToolbar'>"
369         "    <toolitem action='DropperPickAlphaAction' />"
370         "    <toolitem action='DropperSetAlphaAction' />"
371         "  </toolbar>"
373         "  <toolbar name='ConnectorToolbar'>"
374         "    <toolitem action='ConnectorAvoidAction' />"
375         "    <toolitem action='ConnectorIgnoreAction' />"
376         "    <toolitem action='ConnectorSpacingAction' />"
377         "    <toolitem action='ConnectorGraphAction' />"
378         "    <toolitem action='ConnectorLengthAction' />"
379         "    <toolitem action='ConnectorDirectedAction' />"
380         "    <toolitem action='ConnectorOverlapAction' />"
381         "  </toolbar>"
383         "</ui>"
386 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
388 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
390 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
391 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
393 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
394 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
396 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
397 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
399 /* Global text entry widgets necessary for update */
400 /* GtkWidget *dropper_rgb_entry,
401           *dropper_opacity_entry ; */
402 // should be made a private member once this is converted to class
404 static void delete_connection(GObject *obj, sigc::connection *connection) {
405     connection->disconnect();
406     delete connection;
409 static void purge_repr_listener( GObject* obj, GObject* tbl )
411     (void)obj;
412     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
413     if (oldrepr) { // remove old listener
414         sp_repr_remove_listener_by_data(oldrepr, tbl);
415         Inkscape::GC::release(oldrepr);
416         oldrepr = 0;
417         g_object_set_data( tbl, "repr", NULL );
418     }
421 GtkWidget *
422 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
423                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
424                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
426     SPAction *action = verb->get_action(view);
427     if (!action) return NULL;
429     SPAction *doubleclick_action;
430     if (doubleclick_verb)
431         doubleclick_action = doubleclick_verb->get_action(view);
432     else
433         doubleclick_action = NULL;
435     /* fixme: Handle sensitive/unsensitive */
436     /* fixme: Implement sp_button_new_from_action */
437     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
438     gtk_widget_show(b);
439     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
441     return b;
444 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
445                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
447     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
450 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
451                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
453     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
457 static void trigger_sp_action( GtkAction* act, gpointer user_data )
459     SPAction* targetAction = SP_ACTION(user_data);
460     if ( targetAction ) {
461         sp_action_perform( targetAction, NULL );
462     }
465 static void sp_action_action_set_sensitive (SPAction *action, unsigned int sensitive, void *data)
467     if ( data ) {
468         GtkAction* act = GTK_ACTION(data);
469         gtk_action_set_sensitive( act, sensitive );
470     }
473 static SPActionEventVector action_event_vector = {
474     {NULL},
475     NULL,
476     NULL,
477     sp_action_action_set_sensitive,
478     NULL,
479     NULL
480 };
482 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
484     GtkAction* act = 0;
486     SPAction* targetAction = verb->get_action(view);
487     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
488     act = GTK_ACTION(inky);
489     gtk_action_set_sensitive( act, targetAction->sensitive );
491     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
493     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
494     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
496     return act;
499 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
501     Inkscape::UI::View::View *view = desktop;
502     gint verbsToUse[] = {
503         // disabled until we have icons for them:
504         //find
505         //SP_VERB_EDIT_TILE,
506         //SP_VERB_EDIT_UNTILE,
507         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
508         SP_VERB_DIALOG_DISPLAY,
509         SP_VERB_DIALOG_FILL_STROKE,
510         SP_VERB_DIALOG_NAMEDVIEW,
511         SP_VERB_DIALOG_TEXT,
512         SP_VERB_DIALOG_XML_EDITOR,
513         SP_VERB_EDIT_CLONE,
514         SP_VERB_EDIT_COPY,
515         SP_VERB_EDIT_CUT,
516         SP_VERB_EDIT_DUPLICATE,
517         SP_VERB_EDIT_PASTE,
518         SP_VERB_EDIT_REDO,
519         SP_VERB_EDIT_UNDO,
520         SP_VERB_EDIT_UNLINK_CLONE,
521         SP_VERB_FILE_EXPORT,
522         SP_VERB_FILE_IMPORT,
523         SP_VERB_FILE_NEW,
524         SP_VERB_FILE_OPEN,
525         SP_VERB_FILE_PRINT,
526         SP_VERB_FILE_SAVE,
527         SP_VERB_OBJECT_TO_CURVE,
528         SP_VERB_SELECTION_GROUP,
529         SP_VERB_SELECTION_OUTLINE,
530         SP_VERB_SELECTION_UNGROUP,
531         SP_VERB_ZOOM_1_1,
532         SP_VERB_ZOOM_1_2,
533         SP_VERB_ZOOM_2_1,
534         SP_VERB_ZOOM_DRAWING,
535         SP_VERB_ZOOM_IN,
536         SP_VERB_ZOOM_NEXT,
537         SP_VERB_ZOOM_OUT,
538         SP_VERB_ZOOM_PAGE,
539         SP_VERB_ZOOM_PAGE_WIDTH,
540         SP_VERB_ZOOM_PREV,
541         SP_VERB_ZOOM_SELECTION,
542     };
544     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
545     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
547     static std::map<SPDesktop*, GtkActionGroup*> groups;
548     GtkActionGroup* mainActions = 0;
549     if ( groups.find(desktop) != groups.end() ) {
550         mainActions = groups[desktop];
551     }
553     if ( !mainActions ) {
554         mainActions = gtk_action_group_new("main");
555         groups[desktop] = mainActions;
556     }
558     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
559         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
560         if ( verb ) {
561             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
562                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
563                 gtk_action_group_add_action( mainActions, act );
564             }
565         }
566     }
568     return mainActions;
572 GtkWidget *
573 sp_tool_toolbox_new()
575     GtkTooltips *tt = gtk_tooltips_new();
576     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
578     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
579     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
581     gtk_widget_set_sensitive(tb, FALSE);
583     GtkWidget *hb = gtk_handle_box_new();
584     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
585     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
586     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
588     gtk_container_add(GTK_CONTAINER(hb), tb);
589     gtk_widget_show(GTK_WIDGET(tb));
591     sigc::connection* conn = new sigc::connection;
592     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
594     return hb;
597 static void
598 aux_toolbox_attached(GtkHandleBox *toolbox, GtkWidget *child)
600     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
601     gtk_widget_queue_resize(child);
604 static void
605 aux_toolbox_detached(GtkHandleBox *toolbox, GtkWidget *child)
607     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
608     gtk_widget_queue_resize(child);
611 GtkWidget *
612 sp_aux_toolbox_new()
614     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
616     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
617     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
618     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
619     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
620     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
622     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
623     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
625     gtk_widget_set_sensitive(tb, FALSE);
627     GtkWidget *hb = gtk_handle_box_new();
628     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
629     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
630     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
632     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
633     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
635     gtk_container_add(GTK_CONTAINER(hb), tb);
636     gtk_widget_show(GTK_WIDGET(tb));
638     sigc::connection* conn = new sigc::connection;
639     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
641     return hb;
644 //####################################
645 //# Commands Bar
646 //####################################
648 GtkWidget *
649 sp_commands_toolbox_new()
651     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
653     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
654     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
655     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
656     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
657     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
659     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
660     gtk_widget_set_sensitive(tb, FALSE);
662     GtkWidget *hb = gtk_handle_box_new();
663     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
664     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
665     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
667     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
668     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
670     gtk_container_add(GTK_CONTAINER(hb), tb);
671     gtk_widget_show(GTK_WIDGET(tb));
673     sigc::connection* conn = new sigc::connection;
674     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
676     return hb;
680 //####################################
681 //# node editing callbacks
682 //####################################
684 /**
685  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
686  */
687 static ShapeEditor *get_current_shape_editor()
689     if (!SP_ACTIVE_DESKTOP) {
690         return NULL;
691     }
693     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
695     if (!SP_IS_NODE_CONTEXT(event_context)) {
696         return NULL;
697     }
699     return SP_NODE_CONTEXT(event_context)->shape_editor;
703 void
704 sp_node_path_edit_add(void)
706     ShapeEditor *shape_editor = get_current_shape_editor();
707     if (shape_editor) shape_editor->add_node();
710 void
711 sp_node_path_edit_delete(void)
713     ShapeEditor *shape_editor = get_current_shape_editor();
714     if (shape_editor) shape_editor->delete_nodes();
717 void
718 sp_node_path_edit_delete_segment(void)
720     ShapeEditor *shape_editor = get_current_shape_editor();
721     if (shape_editor) shape_editor->delete_segment();
724 void
725 sp_node_path_edit_break(void)
727     ShapeEditor *shape_editor = get_current_shape_editor();
728     if (shape_editor) shape_editor->break_at_nodes();
731 void
732 sp_node_path_edit_join(void)
734     ShapeEditor *shape_editor = get_current_shape_editor();
735     if (shape_editor) shape_editor->join_nodes();
738 void
739 sp_node_path_edit_join_segment(void)
741     ShapeEditor *shape_editor = get_current_shape_editor();
742     if (shape_editor) shape_editor->join_segments();
745 void
746 sp_node_path_edit_toline(void)
748     ShapeEditor *shape_editor = get_current_shape_editor();
749     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
752 void
753 sp_node_path_edit_tocurve(void)
755     ShapeEditor *shape_editor = get_current_shape_editor();
756     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
759 void
760 sp_node_path_edit_cusp(void)
762     ShapeEditor *shape_editor = get_current_shape_editor();
763     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
766 void
767 sp_node_path_edit_smooth(void)
769     ShapeEditor *shape_editor = get_current_shape_editor();
770     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
773 void
774 sp_node_path_edit_symmetrical(void)
776     ShapeEditor *shape_editor = get_current_shape_editor();
777     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
780 static void toggle_show_handles (GtkToggleAction *act, gpointer data) {
781     bool show = gtk_toggle_action_get_active( act );
782     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
783     ShapeEditor *shape_editor = get_current_shape_editor();
784     if (shape_editor) shape_editor->show_handles(show);
787 //################################
788 //##    Node Editing Toolbox    ##
789 //################################
791 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
793     {
794         InkAction* inky = ink_action_new( "NodeInsertAction",
795                                           _("Insert"),
796                                           _("Insert new nodes into selected segments"),
797                                           "node_insert",
798                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
799         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
800         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
801     }
803     {
804         InkAction* inky = ink_action_new( "NodeDeleteAction",
805                                           _("Delete"),
806                                           _("Delete selected nodes"),
807                                           "node_delete",
808                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
809         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
810         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
811     }
813     {
814         InkAction* inky = ink_action_new( "NodeJoinAction",
815                                           _("Join"),
816                                           _("Join selected endnodes"),
817                                           "node_join",
818                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
819         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
820         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
821     }
823     {
824         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
825                                           _("Join Segment"),
826                                           _("Join selected endnodes with a new segment"),
827                                           "node_join_segment",
828                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
829         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
830         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
831     }
833     {
834         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
835                                           _("Delete Segment"),
836                                           _("Split path between two non-endpoint nodes"),
837                                           "node_delete_segment",
838                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
839         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
840         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
841     }
843     {
844         InkAction* inky = ink_action_new( "NodeBreakAction",
845                                           _("Node Break"),
846                                           _("Break path at selected nodes"),
847                                           "node_break",
848                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
849         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
850         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
851     }
853     {
854         InkAction* inky = ink_action_new( "NodeCuspAction",
855                                           _("Node Cusp"),
856                                           _("Make selected nodes corner"),
857                                           "node_cusp",
858                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
859         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
860         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
861     }
863     {
864         InkAction* inky = ink_action_new( "NodeSmoothAction",
865                                           _("Node Smooth"),
866                                           _("Make selected nodes smooth"),
867                                           "node_smooth",
868                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
869         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
870         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
871     }
873     {
874         InkAction* inky = ink_action_new( "NodeSymmetricAction",
875                                           _("Node Symmetric"),
876                                           _("Make selected nodes symmetric"),
877                                           "node_symmetric",
878                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
879         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
880         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
881     }
883     {
884         InkAction* inky = ink_action_new( "NodeLineAction",
885                                           _("Node Line"),
886                                           _("Make selected segments lines"),
887                                           "node_line",
888                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
889         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
890         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
891     }
893     {
894         InkAction* inky = ink_action_new( "NodeCurveAction",
895                                           _("Node Curve"),
896                                           _("Make selected segments curves"),
897                                           "node_curve",
898                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
899         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
900         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
901     }
903     {
904         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
905                                                       _("Show Handles"),
906                                                       _("Show the Bezier handles of selected nodes"),
907                                                       "nodes_show_handles",
908                                                       Inkscape::ICON_SIZE_DECORATION );
909         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
910         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
911         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
912     }
914 } // end of sp_node_toolbox_prep()
917 //########################
918 //##    Zoom Toolbox    ##
919 //########################
921 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
923     // no custom GtkAction setup needed
924 } // end of sp_zoom_toolbox_prep()
926 void
927 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
929     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")));
933 void
934 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
936     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")));
939 void
940 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
942     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")));
945 static void
946 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
948     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
949     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
951     if (old_desktop) {
952         GList *children, *iter;
954         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
955         for ( iter = children ; iter ; iter = iter->next ) {
956             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
957         }
958         g_list_free(children);
959     }
961     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
963     if (desktop) {
964         gtk_widget_set_sensitive(toolbox, TRUE);
965         setup_func(toolbox, desktop);
966         update_func(desktop, desktop->event_context, toolbox);
967         *conn = desktop->connectEventContextChanged
968             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
969     } else {
970         gtk_widget_set_sensitive(toolbox, FALSE);
971     }
973 } // end of toolbox_set_desktop()
976 static void
977 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
979     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
980     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
981     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
982         // "toolbox.tools" was not set. Fallback to older value
983         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
985         // Copy the setting forwards
986         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
987     }
988     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
990     for (int i = 0 ; tools[i].type_name ; i++ ) {
991         GtkWidget *button =
992             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
993                                                               SP_BUTTON_TYPE_TOGGLE,
994                                                               Inkscape::Verb::get(tools[i].verb),
995                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
996                                                               desktop,
997                                                               tooltips );
999         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1000                            (gpointer)button );
1001     }
1005 static void
1006 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
1008     gchar const *const tname = ( eventcontext
1009                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1010                                  : NULL );
1011     for (int i = 0 ; tools[i].type_name ; i++ ) {
1012         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1013         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1014     }
1017 static void
1018 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1020     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1021     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1022     GtkUIManager* mgr = gtk_ui_manager_new();
1023     GError* errVal = 0;
1024     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1025     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1027     std::map<std::string, GtkWidget*> dataHolders;
1029     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1030         if ( aux_toolboxes[i].prep_func ) {
1031             // converted to GtkActions and UIManager
1033             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1034             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1035             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1036             dataHolders[aux_toolboxes[i].type_name] = kludge;
1037             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1038         } else {
1040             GtkWidget *sub_toolbox = 0;
1041             if (aux_toolboxes[i].create_func == NULL)
1042                 sub_toolbox = sp_empty_toolbox_new(desktop);
1043             else {
1044                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1045             }
1047             gtk_size_group_add_widget( grouper, sub_toolbox );
1049             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1050             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1052         }
1053     }
1055     // Second pass to create toolbars *after* all GtkActions are created
1056     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1057         if ( aux_toolboxes[i].prep_func ) {
1058             // converted to GtkActions and UIManager
1060             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1062             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1063             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1065             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1066             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1067             g_free( tmp );
1068             tmp = 0;
1070             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1071             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1072             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1073                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1074             }
1075             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1078             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1080             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1081                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1082                 swatch->setDesktop( desktop );
1083                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1084                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1085                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1086                 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 );
1087             }
1089             gtk_widget_show_all( holder );
1090             sp_set_font_size_smaller( holder );
1092             gtk_size_group_add_widget( grouper, holder );
1094             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1095             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1096         }
1097     }
1099     g_object_unref( G_OBJECT(grouper) );
1102 static void
1103 update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1105     gchar const *tname = ( eventcontext
1106                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1107                            : NULL );
1108     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1109         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1110         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1111             gtk_widget_show_all(sub_toolbox);
1112             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1113         } else {
1114             gtk_widget_hide(sub_toolbox);
1115         }
1116     }
1119 static void
1120 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1122     gchar const * descr =
1123         "<ui>"
1124         "  <toolbar name='CommandsToolbar'>"
1125         "    <toolitem action='FileNew' />"
1126         "    <toolitem action='FileOpen' />"
1127         "    <toolitem action='FileSave' />"
1128         "    <toolitem action='FilePrint' />"
1129         "    <separator />"
1130         "    <toolitem action='FileImport' />"
1131         "    <toolitem action='FileExport' />"
1132         "    <separator />"
1133         "    <toolitem action='EditUndo' />"
1134         "    <toolitem action='EditRedo' />"
1135         "    <separator />"
1136         "    <toolitem action='EditCopy' />"
1137         "    <toolitem action='EditCut' />"
1138         "    <toolitem action='EditPaste' />"
1139         "    <separator />"
1140         "    <toolitem action='ZoomSelection' />"
1141         "    <toolitem action='ZoomDrawing' />"
1142         "    <toolitem action='ZoomPage' />"
1143         "    <separator />"
1144         "    <toolitem action='EditDuplicate' />"
1145         "    <toolitem action='EditClone' />"
1146         "    <toolitem action='EditUnlinkClone' />"
1147         "    <separator />"
1148         "    <toolitem action='SelectionGroup' />"
1149         "    <toolitem action='SelectionUnGroup' />"
1150         "    <separator />"
1151         "    <toolitem action='DialogFillStroke' />"
1152         "    <toolitem action='DialogText' />"
1153         "    <toolitem action='DialogXMLEditor' />"
1154         "    <toolitem action='DialogAlignDistribute' />"
1155         "    <separator />"
1156         "    <toolitem action='DialogPreferences' />"
1157         "    <toolitem action='DialogDocumentProperties' />"
1158         "  </toolbar>"
1159         "</ui>";
1160     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1163     GtkUIManager* mgr = gtk_ui_manager_new();
1164     GError* errVal = 0;
1166     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1167     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1169     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1170     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1171         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1172     }
1173     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1174     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1175     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1178     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1181 static void
1182 update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1186 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1188     gtk_widget_show(toolbox_toplevel);
1189     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1191     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1192     if (!shown_toolbox) {
1193         return;
1194     }
1195     gtk_widget_show(toolbox);
1197     // need to show the spacer, or the padding will be off
1198     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1199     gtk_widget_show(spacer);
1201     gtk_widget_show_all(shown_toolbox);
1204 void
1205 aux_toolbox_space(GtkWidget *tb, gint space)
1207     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1210 static GtkWidget *
1211 sp_empty_toolbox_new(SPDesktop *desktop)
1213     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1214     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1215     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1217     gtk_widget_show_all(tbl);
1218     sp_set_font_size_smaller (tbl);
1220     return tbl;
1223 // helper UI functions
1225 GtkWidget *
1226 sp_tb_spinbutton(
1227     gchar *label, gchar const *tooltip,
1228     gchar const *path, gchar const *data, gdouble def,
1229     GtkWidget *us,
1230     GtkWidget *tbl,
1231     gboolean altx, gchar const *altx_mark,
1232     gdouble lower, gdouble upper, gdouble step, gdouble page,
1233     void (*callback)(GtkAdjustment *, GtkWidget *),
1234     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1236     GtkTooltips *tt = gtk_tooltips_new();
1238     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1240     GtkWidget *l = gtk_label_new(label);
1241     gtk_widget_show(l);
1242     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1243     gtk_container_add(GTK_CONTAINER(hb), l);
1245     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1246                                       lower, upper, step, page, page);
1247     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1248     if (us)
1249         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1251     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1252     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1253     if (altx)
1254         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1255     gtk_widget_set_size_request(sb,
1256                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1257                                 AUX_SPINBUTTON_HEIGHT);
1258     gtk_widget_show(sb);
1259     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1260     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1261     gtk_container_add(GTK_CONTAINER(hb), sb);
1262     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1264     return hb;
1267 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1268                                                        gchar const *label, gchar const *tooltip,
1269                                                        gchar const *path, gchar const *data, gdouble def,
1270                                                        GtkWidget *focusTarget,
1271                                                        GtkWidget *us,
1272                                                        GObject *dataKludge,
1273                                                        gboolean altx, gchar const *altx_mark,
1274                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1275                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1276                                                        void (*callback)(GtkAdjustment *, GObject *),
1277                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1279     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
1280                                                              lower, upper, step, page, page ) );
1281     if (us) {
1282         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1283     }
1285     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1287     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1289     if ( (descrCount > 0) && descrLabels && descrValues ) {
1290         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1291     }
1293     if ( focusTarget ) {
1294         ege_adjustment_action_set_focuswidget( act, focusTarget );
1295     }
1297     if ( altx && altx_mark ) {
1298         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1299     }
1301     if ( dataKludge ) {
1302         g_object_set_data( dataKludge, data, adj );
1303     }
1305     // Using a cast just to make sure we pass in the right kind of function pointer
1306     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1308     return act;
1312 #define MODE_LABEL_WIDTH 70
1314 //########################
1315 //##       Star         ##
1316 //########################
1318 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1320     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1322     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1323         // do not remember prefs if this call is initiated by an undo change, because undoing object
1324         // creation sets bogus values to its attributes before it is deleted
1325         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1326     }
1328     // quit if run by the attr_changed listener
1329     if (g_object_get_data( dataKludge, "freeze" )) {
1330         return;
1331     }
1333     // in turn, prevent listener from responding
1334     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1336     bool modmade = false;
1338     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1339     GSList const *items = selection->itemList();
1340     for (; items != NULL; items = items->next) {
1341         if (SP_IS_STAR((SPItem *) items->data)) {
1342             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1343             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1344             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1345                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1346                                     + M_PI / (gint)adj->value));
1347             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1348             modmade = true;
1349         }
1350     }
1351     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1352                                    _("Star: Change number of corners"));
1354     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1357 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1359     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1361     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1362         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1363     }
1365     // quit if run by the attr_changed listener
1366     if (g_object_get_data( dataKludge, "freeze" )) {
1367         return;
1368     }
1370     // in turn, prevent listener from responding
1371     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1373     bool modmade = false;
1374     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1375     GSList const *items = selection->itemList();
1376     for (; items != NULL; items = items->next) {
1377         if (SP_IS_STAR((SPItem *) items->data)) {
1378             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1380             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1381             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1382             if (r2 < r1) {
1383                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1384             } else {
1385                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1386             }
1388             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1389             modmade = true;
1390         }
1391     }
1393     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1394                                    _("Star: Change spoke ratio"));
1396     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1399 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1401     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1402     bool flat = ege_select_one_action_get_active( act ) == 0;
1404     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1405         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1406                                     flat ? "true" : "false" );
1407     }
1409     // quit if run by the attr_changed listener
1410     if (g_object_get_data( dataKludge, "freeze" )) {
1411         return;
1412     }
1414     // in turn, prevent listener from responding
1415     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1417     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1418     GSList const *items = selection->itemList();
1419     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1420     bool modmade = false;
1422     if ( prop_action ) {
1423         gtk_action_set_sensitive( prop_action, !flat );
1424     }
1426     for (; items != NULL; items = items->next) {
1427         if (SP_IS_STAR((SPItem *) items->data)) {
1428             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1429             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1430             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1431             modmade = true;
1432         }
1433     }
1435     if (modmade) {
1436         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1437                          flat ? _("Make polygon") : _("Make star"));
1438     }
1440     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1443 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1445     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1447     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1448         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1449     }
1451     // quit if run by the attr_changed listener
1452     if (g_object_get_data( dataKludge, "freeze" )) {
1453         return;
1454     }
1456     // in turn, prevent listener from responding
1457     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1459     bool modmade = false;
1461     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1462     GSList const *items = selection->itemList();
1463     for (; items != NULL; items = items->next) {
1464         if (SP_IS_STAR((SPItem *) items->data)) {
1465             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1466             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1467             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1468             modmade = true;
1469         }
1470     }
1471     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1472                                    _("Star: Change rounding"));
1474     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1477 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1479     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1481     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1482         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1483     }
1485     // quit if run by the attr_changed listener
1486     if (g_object_get_data( dataKludge, "freeze" )) {
1487         return;
1488     }
1490     // in turn, prevent listener from responding
1491     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1493     bool modmade = false;
1495     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1496     GSList const *items = selection->itemList();
1497     for (; items != NULL; items = items->next) {
1498         if (SP_IS_STAR((SPItem *) items->data)) {
1499             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1500             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1501             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1502             modmade = true;
1503         }
1504     }
1505     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1506                                    _("Star: Change randomization"));
1508     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1512 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1513                                        gchar const *old_value, gchar const *new_value,
1514                                        bool is_interactive, gpointer data)
1516     GtkWidget *tbl = GTK_WIDGET(data);
1518     // quit if run by the _changed callbacks
1519     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1520         return;
1521     }
1523     // in turn, prevent callbacks from responding
1524     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1526     GtkAdjustment *adj = 0;
1528     if (!strcmp(name, "inkscape:randomized")) {
1529         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1530         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1531     } else if (!strcmp(name, "inkscape:rounded")) {
1532         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1533         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1534     } else if (!strcmp(name, "inkscape:flatsided")) {
1535         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1536         char const *flatsides = repr->attribute("inkscape:flatsided");
1537         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1538         if ( flatsides && !strcmp(flatsides,"false") ) {
1539             ege_select_one_action_set_active( flat_action, 1 );
1540             gtk_action_set_sensitive( prop_action, TRUE );
1541         } else {
1542             ege_select_one_action_set_active( flat_action, 0 );
1543             gtk_action_set_sensitive( prop_action, FALSE );
1544         }
1545     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1546         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1547         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1548         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1549         if (r2 < r1) {
1550             gtk_adjustment_set_value(adj, r2/r1);
1551         } else {
1552             gtk_adjustment_set_value(adj, r1/r2);
1553         }
1554     } else if (!strcmp(name, "sodipodi:sides")) {
1555         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1556         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1557     }
1559     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1563 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1565     NULL, /* child_added */
1566     NULL, /* child_removed */
1567     star_tb_event_attr_changed,
1568     NULL, /* content_changed */
1569     NULL  /* order_changed */
1570 };
1573 /**
1574  *  \param selection Should not be NULL.
1575  */
1576 static void
1577 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1579     int n_selected = 0;
1580     Inkscape::XML::Node *repr = NULL;
1582     purge_repr_listener( tbl, tbl );
1584     for (GSList const *items = selection->itemList();
1585          items != NULL;
1586          items = items->next)
1587     {
1588         if (SP_IS_STAR((SPItem *) items->data)) {
1589             n_selected++;
1590             repr = SP_OBJECT_REPR((SPItem *) items->data);
1591         }
1592     }
1594     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1596     if (n_selected == 0) {
1597         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1598     } else if (n_selected == 1) {
1599         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1601         if (repr) {
1602             g_object_set_data( tbl, "repr", repr );
1603             Inkscape::GC::anchor(repr);
1604             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1605             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1606         }
1607     } else {
1608         // FIXME: implement averaging of all parameters for multiple selected stars
1609         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1610         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1611     }
1615 static void sp_stb_defaults( GtkWidget *widget, GObject *dataKludge )
1617     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1618     // callbacks to lump all the changes for all selected objects in one undo step
1620     GtkAdjustment *adj = 0;
1622     // fixme: make settable in prefs!
1623     gint mag = 5;
1624     gdouble prop = 0.5;
1625     gboolean flat = FALSE;
1626     gdouble randomized = 0;
1627     gdouble rounded = 0;
1629     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1630     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1632     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1633     gtk_action_set_sensitive( sb2, !flat );
1635     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1636     gtk_adjustment_set_value(adj, mag);
1637     gtk_adjustment_value_changed(adj);
1639     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1640     gtk_adjustment_set_value(adj, prop);
1641     gtk_adjustment_value_changed(adj);
1643     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1644     gtk_adjustment_set_value(adj, rounded);
1645     gtk_adjustment_value_changed(adj);
1647     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1648     gtk_adjustment_set_value(adj, randomized);
1649     gtk_adjustment_value_changed(adj);
1653 void
1654 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1656     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1657     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1658     GtkWidget *l = gtk_label_new(NULL);
1659     gtk_label_set_markup(GTK_LABEL(l), title);
1660     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1661     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1662     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1666 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1668     {
1669         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1670         ege_output_action_set_use_markup( act, TRUE );
1671         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1672         g_object_set_data( holder, "mode_action", act );
1673     }
1675     {
1676         //EgeAdjustmentAction* calligraphy_angle = 0;
1677         EgeAdjustmentAction* eact = 0;
1678         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1679         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1681         /* Flatsided checkbox */
1682         {
1683             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1685             GtkTreeIter iter;
1686             gtk_list_store_append( model, &iter );
1687             gtk_list_store_set( model, &iter,
1688                                 0, _("Polygon"),
1689                                 1, _("Regular polygon (with one handle) instead of a star"),
1690                                 2, "star_flat",
1691                                 -1 );
1693             gtk_list_store_append( model, &iter );
1694             gtk_list_store_set( model, &iter,
1695                                 0, _("Star"),
1696                                 1, _("Star instead of a regular polygon (with one handle)"),
1697                                 2, "star_angled",
1698                                 -1 );
1700             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
1701             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1702             g_object_set_data( holder, "flat_action", act );
1704             ege_select_one_action_set_appearance( act, "full" );
1705             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1706             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1707             ege_select_one_action_set_icon_column( act, 2 );
1708             ege_select_one_action_set_tooltip_column( act, 1  );
1710             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1711             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1712         }
1714         /* Magnitude */
1715         //gchar const* labels[] = {_("tri (default)"), _("quad"), _("pent")};
1716         //gdouble values[] = {3, 4, 5};
1717         eact = create_adjustment_action( "MagnitudeAction",
1718                                          _("Corners:"), _("Number of corners of a polygon or star"),
1719                                          "tools.shapes.star", "magnitude", 3,
1720                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1721                                          3, 1024, 1, 5,
1722                                          0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
1723                                          sp_stb_magnitude_value_changed,
1724                                          1.0, 0 );
1725         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1726         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1728         /* Spoke ratio */
1729         //gchar const* labels2[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1730         //gdouble values2[] = {-90, 0, 30, 90};
1731         eact = create_adjustment_action( "SpokeAction",
1732                                          _("Spoke ratio:"),
1733                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1734                                          // Base radius is the same for the closest handle.
1735                                          _("Base radius to tip radius ratio"),
1736                                          "tools.shapes.star", "proportion", 0.5,
1737                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1738                                          0.01, 1.0, 0.01, 0.1,
1739                                          0, 0, 0, // labels2, values2, G_N_ELEMENTS(labels2),
1740                                          sp_stb_proportion_value_changed );
1741         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1742         g_object_set_data( holder, "prop_action", eact );
1744         if ( !isFlatSided ) {
1745             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1746         } else {
1747             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1748         }
1750         /* Roundedness */
1751         //gchar const* labels3[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1752         //gdouble values3[] = {-90, 0, 30, 90};
1753         eact = create_adjustment_action( "RoundednessAction",
1754                                          _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1755                                          "tools.shapes.star", "rounded", 0.0,
1756                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1757                                          -100.0, 100.0, 0.01, 0.1,
1758                                          0, 0, 0, // labels3, values3, G_N_ELEMENTS(labels3),
1759                                          sp_stb_rounded_value_changed );
1760         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1761         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1763         /* Randomization */
1764         //gchar const* labels4[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1765         //gdouble values4[] = {-90, 0, 30, 90};
1766         eact = create_adjustment_action( "RandomizationAction",
1767                                          _("Randomized:"), _("Scatter randomly the corners and angles"),
1768                                          "tools.shapes.star", "randomized", 0.0,
1769                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1770                                          -10.0, 10.0, 0.001, 0.01,
1771                                          0, 0, 0, // labels4, values4, G_N_ELEMENTS(labels4),
1772                                          sp_stb_randomized_value_changed, 0.1, 3 );
1773         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1774         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1775     }
1777     {
1778         /* Reset */
1779         {
1780             GtkAction* act = gtk_action_new( "StarResetAction",
1781                                              _("Defaults"),
1782                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1783                                              GTK_STOCK_CLEAR );
1784             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1785             gtk_action_group_add_action( mainActions, act );
1786             gtk_action_set_sensitive( act, TRUE );
1787         }
1788     }
1790     sigc::connection *connection = new sigc::connection(
1791         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1792         );
1793     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1794     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1798 //########################
1799 //##       Rect         ##
1800 //########################
1802 static void sp_rtb_sensitivize( GObject *tbl )
1804     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1805     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1806     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1808     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1809         gtk_action_set_sensitive( not_rounded, FALSE );
1810     } else {
1811         gtk_action_set_sensitive( not_rounded, TRUE );
1812     }
1816 static void
1817 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1818                           void (*setter)(SPRect *, gdouble))
1820     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1822     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1823     SPUnit const *unit = tracker->getActiveUnit();
1825     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1826         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1827     }
1829     // quit if run by the attr_changed listener
1830     if (g_object_get_data( tbl, "freeze" )) {
1831         return;
1832     }
1834     // in turn, prevent listener from responding
1835     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1837     bool modmade = false;
1838     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1839     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1840         if (SP_IS_RECT(items->data)) {
1841             if (adj->value != 0) {
1842                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
1843             } else {
1844                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
1845             }
1846             modmade = true;
1847         }
1848     }
1850     sp_rtb_sensitivize( tbl );
1852     if (modmade) {
1853         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
1854                                    _("Change rectangle"));
1855     }
1857     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1860 static void
1861 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
1863     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
1866 static void
1867 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
1869     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
1872 static void
1873 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
1875     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
1878 static void
1879 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
1881     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
1886 static void
1887 sp_rtb_defaults( GtkWidget *widget, GObject *obj)
1889     GtkAdjustment *adj = 0;
1891     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
1892     gtk_adjustment_set_value(adj, 0.0);
1893     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
1894     gtk_adjustment_value_changed(adj);
1896     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
1897     gtk_adjustment_set_value(adj, 0.0);
1898     gtk_adjustment_value_changed(adj);
1900     sp_rtb_sensitivize( obj );
1903 static void rect_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1904                                        gchar const *old_value, gchar const *new_value,
1905                                        bool is_interactive, gpointer data)
1907     GObject *tbl = G_OBJECT(data);
1909     // quit if run by the _changed callbacks
1910     if (g_object_get_data( tbl, "freeze" )) {
1911         return;
1912     }
1914     // in turn, prevent callbacks from responding
1915     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
1917     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1918     SPUnit const *unit = tracker->getActiveUnit();
1920     gpointer item = g_object_get_data( tbl, "item" );
1921     if (item && SP_IS_RECT(item)) {
1922         {
1923             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
1924             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
1925             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
1926         }
1928         {
1929             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
1930             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
1931             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
1932         }
1934         {
1935             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
1936             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
1937             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
1938         }
1940         {
1941             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
1942             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
1943             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
1944         }
1945     }
1947     sp_rtb_sensitivize( tbl );
1949     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1953 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
1954     NULL, /* child_added */
1955     NULL, /* child_removed */
1956     rect_tb_event_attr_changed,
1957     NULL, /* content_changed */
1958     NULL  /* order_changed */
1959 };
1961 /**
1962  *  \param selection should not be NULL.
1963  */
1964 static void
1965 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1967     int n_selected = 0;
1968     Inkscape::XML::Node *repr = NULL;
1969     SPItem *item = NULL;
1971     if ( g_object_get_data( tbl, "repr" ) ) {
1972         g_object_set_data( tbl, "item", NULL );
1973     }
1974     purge_repr_listener( tbl, tbl );
1976     for (GSList const *items = selection->itemList();
1977          items != NULL;
1978          items = items->next) {
1979         if (SP_IS_RECT((SPItem *) items->data)) {
1980             n_selected++;
1981             item = (SPItem *) items->data;
1982             repr = SP_OBJECT_REPR(item);
1983         }
1984     }
1986     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1988     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
1990     if (n_selected == 0) {
1991         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1993         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
1994         gtk_action_set_sensitive(w, FALSE);
1995         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
1996         gtk_action_set_sensitive(h, FALSE);
1998     } else if (n_selected == 1) {
1999         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2000         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2002         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2003         gtk_action_set_sensitive(w, TRUE);
2004         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2005         gtk_action_set_sensitive(h, TRUE);
2007         if (repr) {
2008             g_object_set_data( tbl, "repr", repr );
2009             g_object_set_data( tbl, "item", item );
2010             Inkscape::GC::anchor(repr);
2011             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2012             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2013         }
2014     } else {
2015         // FIXME: implement averaging of all parameters for multiple selected
2016         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2017         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2018         sp_rtb_sensitivize( tbl );
2019     }
2023 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2025     EgeAdjustmentAction* eact = 0;
2027     {
2028         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2029         ege_output_action_set_use_markup( act, TRUE );
2030         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2031         g_object_set_data( holder, "mode_action", act );
2032     }
2034     // rx/ry units menu: create
2035     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2036     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2037     // fixme: add % meaning per cent of the width/height
2038     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2039     g_object_set_data( holder, "tracker", tracker );
2041     /* W */
2042     {
2043         eact = create_adjustment_action( "RectWidthAction",
2044                                          _("W:"), _("Width of rectangle"),
2045                                          "tools.shapes.rect", "width", 0,
2046                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2047                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2048                                          0, 0, 0,
2049                                          sp_rtb_width_value_changed );
2050         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2051         g_object_set_data( holder, "width_action", eact );
2052         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2053         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2054     }
2056     /* H */
2057     {
2058         eact = create_adjustment_action( "RectHeightAction",
2059                                          _("H:"), _("Height of rectangle"),
2060                                          "tools.shapes.rect", "height", 0,
2061                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2062                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2063                                          0, 0, 0,
2064                                          sp_rtb_height_value_changed );
2065         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2066         g_object_set_data( holder, "height_action", eact );
2067         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2068         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2069     }
2071     /* rx */
2072     {
2073         eact = create_adjustment_action( "RadiusXAction",
2074                                          _("Rx:"), _("Horizontal radius of rounded corners"),
2075                                          "tools.shapes.rect", "rx", 0,
2076                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2077                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2078                                          0, 0, 0,
2079                                          sp_rtb_rx_value_changed);
2080         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2081         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2082     }
2084     /* ry */
2085     {
2086         eact = create_adjustment_action( "RadiusYAction",
2087                                          _("Ry:"), _("Vertical radius of rounded corners"),
2088                                          "tools.shapes.rect", "ry", 0,
2089                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2090                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2091                                          0, 0, 0,
2092                                          sp_rtb_ry_value_changed);
2093         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2094         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2095     }
2097     // add the units menu
2098     {
2099         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), _("") );
2100         gtk_action_group_add_action( mainActions, act );
2101     }
2103     /* Reset */
2104     {
2105         InkAction* inky = ink_action_new( "RectResetAction",
2106                                           _("Not rounded"),
2107                                           _("Make corners sharp"),
2108                                           "squared_corner",
2109                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2110         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2111         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2112         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2113         g_object_set_data( holder, "not_rounded", inky );
2114     }
2116     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2117     sp_rtb_sensitivize( holder );
2119     sigc::connection *connection = new sigc::connection(
2120         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2121         );
2122     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2123     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2126 //########################
2127 //##       3D Box       ##
2128 //########################
2130 static void sp_3dbox_toggle_vp_changed( GtkToggleAction *act, gpointer data )
2132     SPDocument *document = sp_desktop_document (inkscape_active_desktop ());
2133     Box3D::Axis axis = (Box3D::Axis) GPOINTER_TO_INT(data);
2135     if (document->current_perspective) {
2136         document->current_perspective->toggle_boxes (axis);
2137     }
2138     
2141 static void sp_3dboxtb_handles_state_changed( EgeSelectOneAction *act, GObject *tbl )
2143     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2144     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2145         if ( ege_select_one_action_get_active( act ) != 0 ) {
2146             prefs_set_string_attribute("tools.shapes.3dbox", "constrainedXYmoving", "true");
2147         } else {
2148             prefs_set_string_attribute("tools.shapes.3dbox", "constrainedXYmoving", "false");
2149         }
2150     }
2152     // quit if run by the attr_changed listener
2153     if (g_object_get_data( tbl, "freeze" )) {
2154         return;
2155     }
2157     // in turn, prevent listener from responding
2158     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2160     bool modmade = false;
2162     SPEventContext *ec = SP_EVENT_CONTEXT(inkscape_active_event_context());
2163     if (!SP_IS_3DBOX_CONTEXT(ec)) return;
2164     SP3DBoxContext *bc = SP_3DBOX_CONTEXT(ec);
2165     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2166          items != NULL;
2167          items = items->next)
2168     {
2169         if (SP_IS_3DBOX((SPItem *) items->data)) {
2170             if (ec->shape_knot_holder) {
2171                 sp_knot_holder_destroy(ec->shape_knot_holder);
2172                 if ( ege_select_one_action_get_active(act) != 0 ) {
2173                     bc->number_of_handles = 4;
2174                     ec->shape_knot_holder = sp_3dbox_knot_holder((SPItem *) items->data, SP_ACTIVE_DESKTOP, 4);;
2175                 } else {
2176                     bc->number_of_handles = 3;
2177                     ec->shape_knot_holder = sp_3dbox_knot_holder((SPItem *) items->data, SP_ACTIVE_DESKTOP, 3);;
2178                 }
2179             } else {
2180                 g_print ("Warning: No KnotHolder detected!!!\n");
2181             }
2182             modmade = true;
2183         }
2184     }
2186     if (modmade) {
2187         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
2188                                    _("3D Box: Change number of handles"));
2189     }
2191     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2195 static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2197     SPDocument *document = sp_desktop_document (desktop);
2198     bool toggled = false;
2199     /* toggle VP in X direction */
2200     {
2201     InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXAction",
2202                                                   _("Toggle VP in X direction"),
2203                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2204                                                   "toggle_vp_x",
2205                                                   Inkscape::ICON_SIZE_DECORATION );
2206     gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2207     if (document->current_perspective) {
2208         toggled = !document->current_perspective->get_vanishing_point(Box3D::X)->is_finite();
2209     }
2210     gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
2211     /* we connect the signal after setting the state to avoid switching the state again */
2212     g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), GINT_TO_POINTER(Box3D::X));
2213     }
2215     /* toggle VP in Y direction */
2216     {
2217     InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYAction",
2218                                                   _("Toggle VP in Y direction"),
2219                                                   _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2220                                                   "toggle_vp_y",
2221                                                   Inkscape::ICON_SIZE_DECORATION );
2222     gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2223     if (document->current_perspective) {
2224         toggled = !document->current_perspective->get_vanishing_point(Box3D::Y)->is_finite();
2225     }
2226     gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
2227     /* we connect the signal after setting the state to avoid switching the state again */
2228     g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), GINT_TO_POINTER(Box3D::Y));
2229     }
2231     /* toggle VP in Z direction */
2232     {
2233     InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZAction",
2234                                                   _("Toggle VP in Z direction"),
2235                                                   _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2236                                                   "toggle_vp_z",
2237                                                   Inkscape::ICON_SIZE_DECORATION );
2238     gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2239     if (document->current_perspective) {
2240         toggled = !document->current_perspective->get_vanishing_point(Box3D::Z)->is_finite();
2241     }
2242     /* we connect the signal after setting the state to avoid switching the state again */
2243     gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), toggled );
2244     g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_changed), GINT_TO_POINTER(Box3D::Z));
2245     }
2248     /* Number of handles and resizing behaviour */
2249     {
2250         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2252         GtkTreeIter iter;
2253         gtk_list_store_append( model, &iter );
2254         gtk_list_store_set( model, &iter,
2255                             0, _("Three Handles"),
2256                             1, _("Switch to three handles (arbitrary resizing in XY-direction)"),
2257                             2, "3dbox_three_handles",
2258                             -1 );
2260         gtk_list_store_append( model, &iter );
2261         gtk_list_store_set( model, &iter,
2262                             0, _("Four Handles"),
2263                             1, _("Switch to four handles (constrained resizing in XY-direction)"),
2264                             2, "3dbox_four_handles",
2265                             -1 );
2267         EgeSelectOneAction* act = ege_select_one_action_new( "3DBoxHandlesAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
2268         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2269         g_object_set_data( holder, "handles_action", act );
2271         ege_select_one_action_set_appearance( act, "full" );
2272         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2273         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2274         ege_select_one_action_set_icon_column( act, 2 );
2275         ege_select_one_action_set_tooltip_column( act, 1  );
2277         gchar const *handlestr = prefs_get_string_attribute("tools.shapes.3dbox", "constrainedXYmoving");
2278         bool isConstrained = (!handlestr || (handlestr && !strcmp(handlestr, "true")));
2279         ege_select_one_action_set_active( act, isConstrained ? 0 : 1 );
2280         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_3dboxtb_handles_state_changed), holder );
2281     }
2284 //########################
2285 //##       Spiral       ##
2286 //########################
2288 static void
2289 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2291     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2293     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2294         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2295     }
2297     // quit if run by the attr_changed listener
2298     if (g_object_get_data( tbl, "freeze" )) {
2299         return;
2300     }
2302     // in turn, prevent listener from responding
2303     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2305     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2307     bool modmade = false;
2308     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2309          items != NULL;
2310          items = items->next)
2311     {
2312         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2313             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2314             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2315             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2316             modmade = true;
2317         }
2318     }
2320     g_free(namespaced_name);
2322     if (modmade) {
2323         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2324                                    _("Change spiral"));
2325     }
2327     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2330 static void
2331 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2333     sp_spl_tb_value_changed(adj, tbl, "revolution");
2336 static void
2337 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2339     sp_spl_tb_value_changed(adj, tbl, "expansion");
2342 static void
2343 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2345     sp_spl_tb_value_changed(adj, tbl, "t0");
2348 static void
2349 sp_spl_tb_defaults(GtkWidget *widget, GtkObject *obj)
2351     GtkWidget *tbl = GTK_WIDGET(obj);
2353     GtkAdjustment *adj;
2355     // fixme: make settable
2356     gdouble rev = 5;
2357     gdouble exp = 1.0;
2358     gdouble t0 = 0.0;
2360     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2361     gtk_adjustment_set_value(adj, rev);
2362     gtk_adjustment_value_changed(adj);
2364     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2365     gtk_adjustment_set_value(adj, exp);
2366     gtk_adjustment_value_changed(adj);
2368     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2369     gtk_adjustment_set_value(adj, t0);
2370     gtk_adjustment_value_changed(adj);
2372     spinbutton_defocus(GTK_OBJECT(tbl));
2376 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2377                                          gchar const *old_value, gchar const *new_value,
2378                                          bool is_interactive, gpointer data)
2380     GtkWidget *tbl = GTK_WIDGET(data);
2382     // quit if run by the _changed callbacks
2383     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2384         return;
2385     }
2387     // in turn, prevent callbacks from responding
2388     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2390     GtkAdjustment *adj;
2391     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2392     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2394     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2395     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2397     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2398     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2400     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2404 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2405     NULL, /* child_added */
2406     NULL, /* child_removed */
2407     spiral_tb_event_attr_changed,
2408     NULL, /* content_changed */
2409     NULL  /* order_changed */
2410 };
2412 static void
2413 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2415     int n_selected = 0;
2416     Inkscape::XML::Node *repr = NULL;
2418     purge_repr_listener( tbl, tbl );
2420     for (GSList const *items = selection->itemList();
2421          items != NULL;
2422          items = items->next)
2423     {
2424         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2425             n_selected++;
2426             repr = SP_OBJECT_REPR((SPItem *) items->data);
2427         }
2428     }
2430     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2432     if (n_selected == 0) {
2433         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2434     } else if (n_selected == 1) {
2435         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2437         if (repr) {
2438             g_object_set_data( tbl, "repr", repr );
2439             Inkscape::GC::anchor(repr);
2440             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2441             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2442         }
2443     } else {
2444         // FIXME: implement averaging of all parameters for multiple selected
2445         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2446         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2447     }
2451 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2453     EgeAdjustmentAction* eact = 0;
2455     {
2456         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2457         ege_output_action_set_use_markup( act, TRUE );
2458         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2459         g_object_set_data( holder, "mode_action", act );
2460     }
2462     /* Revolution */
2463     {
2464         eact = create_adjustment_action( "SpiralRevolutionAction",
2465                                          _("Turns:"), _("Number of revolutions"),
2466                                          "tools.shapes.spiral", "revolution", 3.0,
2467                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2468                                          0.01, 1024.0, 0.1, 1.0,
2469                                          0, 0, 0,
2470                                          sp_spl_tb_revolution_value_changed, 1, 2);
2471         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2472     }
2474     /* Expansion */
2475     {
2476         eact = create_adjustment_action( "SpiralExpansionAction",
2477                                          _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2478                                          "tools.shapes.spiral", "expansion", 1.0,
2479                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2480                                          0.0, 1000.0, 0.01, 1.0,
2481                                          0, 0, 0,
2482                                          sp_spl_tb_expansion_value_changed);
2483         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2484     }
2486     /* T0 */
2487     {
2488         eact = create_adjustment_action( "SpiralT0Action",
2489                                          _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2490                                          "tools.shapes.spiral", "t0", 0.0,
2491                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2492                                          0.0, 0.999, 0.01, 1.0,
2493                                          0, 0, 0,
2494                                          sp_spl_tb_t0_value_changed);
2495         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2496     }
2498     /* Reset */
2499     {
2500         InkAction* inky = ink_action_new( "SpiralResetAction",
2501                                           _("Defaults"),
2502                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2503                                           GTK_STOCK_CLEAR,
2504                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2505         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2506         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2507     }
2510     sigc::connection *connection = new sigc::connection(
2511         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2512         );
2513     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2514     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2517 //########################
2518 //##     Pen/Pencil    ##
2519 //########################
2522 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2524     // Put stuff here
2527 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2529     // Put stuff here
2532 //########################
2533 //##       Tweak        ##
2534 //########################
2536 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2538     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2541 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject *tbl )
2543     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2546 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer data )
2548     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2551 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2553     prefs_set_int_attribute("tools.tweak", "mode", ege_select_one_action_get_active( act ));
2556 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject *tbl )
2558     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2562 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2564     {
2565         /* Width */
2566         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2567         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2568         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2569                                                               _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2570                                                               "tools.tweak", "width", 15,
2571                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2572                                                               1, 100, 1.0, 10.0,
2573                                                               labels, values, G_N_ELEMENTS(labels),
2574                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2575         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2576         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2577     }
2580     {
2581         /* Force */
2582         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2583         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2584         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2585                                                               _("Force:"), _("The force of the tweak action"),
2586                                                               "tools.tweak", "force", 20,
2587                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2588                                                               1, 100, 1.0, 10.0,
2589                                                               labels, values, G_N_ELEMENTS(labels),
2590                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2591         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2592         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2593     }
2595     /* Mode */
2596     {
2597         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2599         GtkTreeIter iter;
2600         gtk_list_store_append( model, &iter );
2601         gtk_list_store_set( model, &iter,
2602                             0, _("Push mode"),
2603                             1, _("Switch to Push mode"),
2604                             2, "tweak_push_mode",
2605                             -1 );
2607         gtk_list_store_append( model, &iter );
2608         gtk_list_store_set( model, &iter,
2609                             0, _("Melt mode"),
2610                             1, _("Switch to Melt mode"),
2611                             2, "tweak_suck_mode",
2612                             -1 );
2614         gtk_list_store_append( model, &iter );
2615         gtk_list_store_set( model, &iter,
2616                             0, _("Blow mode"),
2617                             1, _("Switch to Blow mode"),
2618                             2, "tweak_blow_mode",
2619                             -1 );
2621         gtk_list_store_append( model, &iter );
2622         gtk_list_store_set( model, &iter,
2623                             0, _("Roughen mode"),
2624                             1, _("Switch to Roughen mode"),
2625                             2, "tweak_roughen_mode",
2626                             -1 );
2628         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
2629         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2630         g_object_set_data( holder, "mode_action", act );
2632         ege_select_one_action_set_appearance( act, "full" );
2633         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2634         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2635         ege_select_one_action_set_icon_column( act, 2 );
2636         ege_select_one_action_set_tooltip_column( act, 1  );
2638         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
2639         ege_select_one_action_set_active( act, mode );
2640         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
2642         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
2643     }
2645     {   /* Fidelity */
2646         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
2647         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
2648         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
2649                                                               _("Fidelity:"), _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
2650                                                               "tools.tweak", "fidelity", 50,
2651                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
2652                                                               1, 100, 1.0, 10.0,
2653                                                               labels, values, G_N_ELEMENTS(labels),
2654                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
2655         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2656         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2657     }
2660     /* Use Pressure button */
2661     {
2662         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
2663                                                       _("Pressure"),
2664                                                       _("Use the pressure of the input device to alter the width of the area"),
2665                                                       "use_pressure",
2666                                                       Inkscape::ICON_SIZE_DECORATION );
2667         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2668         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
2669         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
2670     }
2675 //########################
2676 //##     Calligraphy    ##
2677 //########################
2679 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
2681     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2684 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
2686     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2689 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
2691     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2694 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2696     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2699 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
2701     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
2704 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
2706     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
2709 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
2711     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
2714 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
2716     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
2719 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer data )
2721     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2724 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer data )
2726     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
2729 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
2731     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
2733     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
2736 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
2738     // FIXME: make defaults settable via Inkscape Options
2739     struct KeyValue {
2740         char const *key;
2741         double value;
2742     } const key_values[] = {
2743         {"mass", 0.02},
2744         {"wiggle", 0.0},
2745         {"angle", 30.0},
2746         {"width", 15},
2747         {"thinning", 0.1},
2748         {"tremor", 0.0},
2749         {"flatness", 0.9},
2750         {"cap_rounding", 0.0}
2751     };
2753     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
2754         KeyValue const &kv = key_values[i];
2755         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
2756         if ( adj ) {
2757             gtk_adjustment_set_value(adj, kv.value);
2758         }
2759     }
2763 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2765     {
2766         EgeAdjustmentAction* calligraphy_angle = 0;
2768         {
2769         /* Width */
2770         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
2771         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2772         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
2773                                          _("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
2774                                          "tools.calligraphic", "width", 15,
2775                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
2776                                          1, 100, 1.0, 10.0,
2777                                          labels, values, G_N_ELEMENTS(labels),
2778                                          sp_ddc_width_value_changed,  0.01, 0, 100 );
2779         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2780         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2781         }
2783         {
2784         /* Thinning */
2785             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
2786             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
2787         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
2788                                          _("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
2789                                          "tools.calligraphic", "thinning", 0.1,
2790                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2791                                          -1.0, 1.0, 0.01, 0.1,
2792                                          labels, values, G_N_ELEMENTS(labels),
2793                                          sp_ddc_velthin_value_changed, 0.01, 2);
2794         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2795         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2796         }
2798         {
2799         /* Angle */
2800         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
2801         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2802         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
2803                                          _("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
2804                                          "tools.calligraphic", "angle", 30,
2805                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
2806                                          -90.0, 90.0, 1.0, 10.0,
2807                                          labels, values, G_N_ELEMENTS(labels),
2808                                          sp_ddc_angle_value_changed, 1, 0 );
2809         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2810         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2811         calligraphy_angle = eact;
2812         }
2814         {
2815         /* Fixation */
2816             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
2817         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
2818         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
2819                                          _("Fixation:"), _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
2820                                          "tools.calligraphic", "flatness", 0.9,
2821                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2822                                          0.0, 1.0, 0.01, 0.1,
2823                                          labels, values, G_N_ELEMENTS(labels),
2824                                          sp_ddc_flatness_value_changed, 0.01, 2 );
2825         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2826         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2827         }
2829         {
2830         /* Cap Rounding */
2831             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
2832         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
2833         // TRANSLATORS: "cap" means "end" (both start and finish) here
2834         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
2835                                          _("Caps:"), _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
2836                                          "tools.calligraphic", "cap_rounding", 0.0,
2837                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2838                                          0.0, 5.0, 0.01, 0.1,
2839                                          labels, values, G_N_ELEMENTS(labels),
2840                                          sp_ddc_cap_rounding_value_changed, 0.01, 2 );
2841         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2842         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2843         }
2845         {
2846         /* Tremor */
2847             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
2848         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
2849         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
2850                                          _("Tremor:"), _("Increase to make strokes rugged and trembling"),
2851                                          "tools.calligraphic", "tremor", 0.0,
2852                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2853                                          0.0, 1.0, 0.01, 0.1,
2854                                          labels, values, G_N_ELEMENTS(labels),
2855                                          sp_ddc_tremor_value_changed, 0.01, 2 );
2857         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2858         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2859         }
2861         {
2862         /* Wiggle */
2863         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
2864         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
2865         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
2866                                          _("Wiggle:"), _("Increase to make the pen waver and wiggle"),
2867                                          "tools.calligraphic", "wiggle", 0.0,
2868                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2869                                          0.0, 1.0, 0.01, 0.1,
2870                                          labels, values, G_N_ELEMENTS(labels),
2871                                          sp_ddc_wiggle_value_changed, 0.01, 2 );
2872         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2873         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2874         }
2876         {
2877         /* Mass */
2878             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
2879         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
2880         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
2881                                          _("Mass:"), _("Increase to make the pen drag behind, as if slowed by inertia"),
2882                                          "tools.calligraphic", "mass", 0.02,
2883                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2884                                          0.0, 1.0, 0.01, 0.1,
2885                                          labels, values, G_N_ELEMENTS(labels),
2886                                          sp_ddc_mass_value_changed, 0.01, 2 );
2887         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2888         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2889         }
2892         /* Trace Background button */
2893         {
2894             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
2895                                                           _("Trace Background"),
2896                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
2897                                                           "trace_background",
2898                                                           Inkscape::ICON_SIZE_DECORATION );
2899             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2900             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
2901             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
2902         }
2904         /* Use Pressure button */
2905         {
2906             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
2907                                                           _("Pressure"),
2908                                                           _("Use the pressure of the input device to alter the width of the pen"),
2909                                                           "use_pressure",
2910                                                           Inkscape::ICON_SIZE_DECORATION );
2911             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2912             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
2913             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
2914         }
2916         /* Use Tilt button */
2917         {
2918             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
2919                                                           _("Tilt"),
2920                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
2921                                                           "use_tilt",
2922                                                           Inkscape::ICON_SIZE_DECORATION );
2923             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2924             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
2925             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2926             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2927         }
2929         /* Reset */
2930         {
2931             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
2932                                              _("Defaults"),
2933                                              _("Reset all parameters to defaults"),
2934                                              GTK_STOCK_CLEAR );
2935             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
2936             gtk_action_group_add_action( mainActions, act );
2937             gtk_action_set_sensitive( act, TRUE );
2938         }
2939     }
2943 //########################
2944 //##    Circle / Arc    ##
2945 //########################
2947 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
2949     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
2950     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
2952     if (v1 == 0 && v2 == 0) {
2953         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
2954             gtk_action_set_sensitive( ocb, FALSE );
2955             gtk_action_set_sensitive( make_whole, FALSE );
2956         }
2957     } else {
2958         gtk_action_set_sensitive( ocb, TRUE );
2959         gtk_action_set_sensitive( make_whole, TRUE );
2960     }
2963 static void
2964 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
2966     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2968     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2969         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
2970     }
2972     // quit if run by the attr_changed listener
2973     if (g_object_get_data( tbl, "freeze" )) {
2974         return;
2975     }
2977     // in turn, prevent listener from responding
2978     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2980     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2982     bool modmade = false;
2983     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2984          items != NULL;
2985          items = items->next)
2986     {
2987         SPItem *item = SP_ITEM(items->data);
2989         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
2991             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
2992             SPArc *arc = SP_ARC(item);
2994             if (!strcmp(value_name, "start"))
2995                 ge->start = (adj->value * M_PI)/ 180;
2996             else
2997                 ge->end = (adj->value * M_PI)/ 180;
2999             sp_genericellipse_normalize(ge);
3000             ((SPObject *)arc)->updateRepr();
3001             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3003             modmade = true;
3004         }
3005     }
3007     g_free(namespaced_name);
3009     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3011     sp_arctb_sensitivize( tbl, adj->value, other->value );
3013     if (modmade) {
3014         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3015                                    _("Arc: Change start/end"));
3016     }
3018     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3022 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3024     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3027 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3029     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3032 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3034     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3035     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3036         if ( ege_select_one_action_get_active( act ) != 0 ) {
3037             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3038         } else {
3039             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3040         }
3041     }
3043     // quit if run by the attr_changed listener
3044     if (g_object_get_data( tbl, "freeze" )) {
3045         return;
3046     }
3048     // in turn, prevent listener from responding
3049     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3051     bool modmade = false;
3053     if ( ege_select_one_action_get_active(act) != 0 ) {
3054         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3055              items != NULL;
3056              items = items->next)
3057         {
3058             if (SP_IS_ARC((SPItem *) items->data)) {
3059                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3060                 repr->setAttribute("sodipodi:open", "true");
3061                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3062                 modmade = true;
3063             }
3064         }
3065     } else {
3066         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3067              items != NULL;
3068              items = items->next)
3069         {
3070             if (SP_IS_ARC((SPItem *) items->data))    {
3071                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3072                 repr->setAttribute("sodipodi:open", NULL);
3073                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3074                 modmade = true;
3075             }
3076         }
3077     }
3079     if (modmade) {
3080         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3081                                    _("Arc: Change open/closed"));
3082     }
3084     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3087 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3089     GtkAdjustment *adj;
3090     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3091     gtk_adjustment_set_value(adj, 0.0);
3092     gtk_adjustment_value_changed(adj);
3094     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3095     gtk_adjustment_set_value(adj, 0.0);
3096     gtk_adjustment_value_changed(adj);
3098     spinbutton_defocus( GTK_OBJECT(obj) );
3101 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
3102                                       gchar const *old_value, gchar const *new_value,
3103                                       bool is_interactive, gpointer data)
3105     GObject *tbl = G_OBJECT(data);
3107     // quit if run by the _changed callbacks
3108     if (g_object_get_data( tbl, "freeze" )) {
3109         return;
3110     }
3112     // in turn, prevent callbacks from responding
3113     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3115     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3116     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3118     GtkAdjustment *adj1,*adj2;
3119     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3120     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3121     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3122     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3124     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3126     char const *openstr = NULL;
3127     openstr = repr->attribute("sodipodi:open");
3128     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3130     if (openstr) {
3131         ege_select_one_action_set_active( ocb, 1 );
3132     } else {
3133         ege_select_one_action_set_active( ocb, 0 );
3134     }
3136     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3139 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3140     NULL, /* child_added */
3141     NULL, /* child_removed */
3142     arc_tb_event_attr_changed,
3143     NULL, /* content_changed */
3144     NULL  /* order_changed */
3145 };
3148 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3150     int n_selected = 0;
3151     Inkscape::XML::Node *repr = NULL;
3153     purge_repr_listener( tbl, tbl );
3155     for (GSList const *items = selection->itemList();
3156          items != NULL;
3157          items = items->next)
3158     {
3159         if (SP_IS_ARC((SPItem *) items->data)) {
3160             n_selected++;
3161             repr = SP_OBJECT_REPR((SPItem *) items->data);
3162         }
3163     }
3165     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3167     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3168     if (n_selected == 0) {
3169         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3170     } else if (n_selected == 1) {
3171         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3172         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3174         if (repr) {
3175             g_object_set_data( tbl, "repr", repr );
3176             Inkscape::GC::anchor(repr);
3177             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3178             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3179         }
3180     } else {
3181         // FIXME: implement averaging of all parameters for multiple selected
3182         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3183         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3184         sp_arctb_sensitivize( tbl, 1, 0 );
3185     }
3189 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3191     EgeAdjustmentAction* eact = 0;
3194     {
3195         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3196         ege_output_action_set_use_markup( act, TRUE );
3197         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3198         g_object_set_data( holder, "mode_action", act );
3199     }
3201     /* Start */
3202     {
3203         eact = create_adjustment_action( "ArcStartAction",
3204                                          _("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
3205                                          "tools.shapes.arc", "start", 0.0,
3206                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3207                                          -360.0, 360.0, 1.0, 10.0,
3208                                          0, 0, 0,
3209                                          sp_arctb_start_value_changed);
3210         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3211     }
3213     /* End */
3214     {
3215         eact = create_adjustment_action( "ArcEndAction",
3216                                          _("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
3217                                          "tools.shapes.arc", "end", 0.0,
3218                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3219                                          -360.0, 360.0, 1.0, 10.0,
3220                                          0, 0, 0,
3221                                          sp_arctb_end_value_changed);
3222         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3223     }
3225     /* Segments / Pie checkbox */
3226     {
3227         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3229         GtkTreeIter iter;
3230         gtk_list_store_append( model, &iter );
3231         gtk_list_store_set( model, &iter,
3232                             0, _("Closed arc"),
3233                             1, _("Switch to segment (closed shape with two radii)"),
3234                             2, "circle_closed_arc",
3235                             -1 );
3237         gtk_list_store_append( model, &iter );
3238         gtk_list_store_set( model, &iter,
3239                             0, _("Open Arc"),
3240                             1, _("Switch to arc (unclosed shape)"),
3241                             2, "circle_open_arc",
3242                             -1 );
3244         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
3245         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3246         g_object_set_data( holder, "open_action", act );
3248         ege_select_one_action_set_appearance( act, "full" );
3249         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3250         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3251         ege_select_one_action_set_icon_column( act, 2 );
3252         ege_select_one_action_set_tooltip_column( act, 1  );
3254         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3255         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3256         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3257         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3258     }
3260     /* Make Whole */
3261     {
3262         InkAction* inky = ink_action_new( "ArcResetAction",
3263                                           _("Make whole"),
3264                                           _("Make the shape a whole ellipse, not arc or segment"),
3265                                           "reset_circle",
3266                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3267         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3268         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3269         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3270         g_object_set_data( holder, "make_whole", inky );
3271     }
3273     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3274     // sensitivize make whole and open checkbox
3275     {
3276         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3277         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3278         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3279     }
3282     sigc::connection *connection = new sigc::connection(
3283         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3284         );
3285     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3286     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3292 // toggle button callbacks and updaters
3294 //########################
3295 //##      Dropper       ##
3296 //########################
3298 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3299     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3300     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3301     if ( set_action ) {
3302         if ( gtk_toggle_action_get_active( act ) ) {
3303             gtk_action_set_sensitive( set_action, TRUE );
3304         } else {
3305             gtk_action_set_sensitive( set_action, FALSE );
3306         }
3307     }
3309     spinbutton_defocus(GTK_OBJECT(tbl));
3312 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3313     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3314     spinbutton_defocus(GTK_OBJECT(tbl));
3318 /**
3319  * Dropper auxiliary toolbar construction and setup.
3320  *
3321  * TODO: Would like to add swatch of current color.
3322  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3323  *       can drag and drop places. Will provide a nice mixing palette.
3324  */
3325 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3327     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3329     {
3330         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3331                                                       _("Pick alpha"),
3332                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3333                                                       "color_alpha_get",
3334                                                       Inkscape::ICON_SIZE_DECORATION );
3335         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3336         g_object_set_data( holder, "pick_action", act );
3337         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3338         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3339     }
3341     {
3342         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3343                                                       _("Set alpha"),
3344                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3345                                                       "color_alpha_set",
3346                                                       Inkscape::ICON_SIZE_DECORATION );
3347         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3348         g_object_set_data( holder, "set_action", act );
3349         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3350         // make sure it's disabled if we're not picking alpha
3351         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3352         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3353     }
3357 //########################
3358 //##    Text Toolbox    ##
3359 //########################
3360 /*
3361 static void
3362 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3364     //Call back for letter sizing spinbutton
3367 static void
3368 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3370     //Call back for line height spinbutton
3373 static void
3374 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3376     //Call back for horizontal kerning spinbutton
3379 static void
3380 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3382     //Call back for vertical kerning spinbutton
3385 static void
3386 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3388     //Call back for letter rotation spinbutton
3389 }*/
3391 namespace {
3393 bool visible = false;
3395 void
3396 sp_text_toolbox_selection_changed (Inkscape::Selection *selection, GObject *tbl)
3398     SPStyle *query =
3399         sp_style_new (SP_ACTIVE_DOCUMENT);
3401     int result_family =
3402         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3404     int result_style =
3405         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3407     int result_numbers =
3408         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3410     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3412     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3413     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3414     {
3415         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3417         if (repr)
3418         {
3419             sp_style_read_from_repr (query, repr);
3420         }
3421         else
3422         {
3423             return;
3424         }
3425     }
3427     if (query->text)
3428     {
3429         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3430             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3431             gtk_entry_set_text (GTK_ENTRY (entry), "");
3433         } else if (query->text->font_family.value) {
3435             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3436             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3438             Gtk::TreePath path;
3439             try {
3440                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3441             } catch (...) {
3442                 return;
3443             }
3445             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3446             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3448             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3450             gtk_tree_selection_select_path (tselection, path.gobj());
3451             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3453             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3454         }
3456         //Size
3457         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3458         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3459         g_object_set_data (tbl, "size-block", gpointer(1));
3460         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3461         g_object_set_data (tbl, "size-block", gpointer(0));
3462         free (str);
3464         //Anchor
3465         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3466         {
3467             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3468             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3469             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3470             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3471         }
3472         else
3473         {
3474             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3475             {
3476                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3477                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3478                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3479                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3480             }
3481             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3482             {
3483                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3484                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3485                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3486                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3487             }
3488             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3489             {
3490                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3491                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3492                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3493                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3494             }
3495         }
3497         //Style
3498         {
3499             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3501             gboolean active = gtk_toggle_button_get_active (button);
3502             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3504             if (active != check)
3505             {
3506                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3507                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3508                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3509             }
3510         }
3512         {
3513             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3515             gboolean active = gtk_toggle_button_get_active (button);
3516             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3518             if (active != check)
3519             {
3520                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3521                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3522                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3523             }
3524         }
3526         //Orientation
3527         //locking both buttons, changing one affect all group (both)
3528         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3529         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3531         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3532         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3534         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3535         {
3536             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3537         }
3538         else
3539         {
3540             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3541         }
3542         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3543         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3544     }
3546     sp_style_unref(query);
3549 void
3550 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint flags, GObject *tbl)
3552     sp_text_toolbox_selection_changed (selection, tbl);
3555 void
3556 sp_text_toolbox_subselection_changed (gpointer dragger, GObject *tbl)
3558     sp_text_toolbox_selection_changed (NULL, tbl);
3561 void
3562 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3563                                 GObject             *tbl)
3565     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3566     GtkTreeModel *model = 0;
3567     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3568     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3569     GtkTreeIter   iter;
3570     char         *family = 0;
3572     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3573     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3575     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3576         return;
3577     }
3579     gtk_tree_model_get (model, &iter, 0, &family, -1);
3581     if (g_object_get_data (G_OBJECT (selection), "block"))
3582     {
3583         gtk_entry_set_text (GTK_ENTRY (entry), family);
3584         return;
3585     }
3587     gtk_widget_hide (popdown);
3588     visible = false;
3590     gtk_entry_set_text (GTK_ENTRY (entry), family);
3592     SPStyle *query =
3593         sp_style_new (SP_ACTIVE_DOCUMENT);
3595     int result_numbers =
3596         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3598     SPCSSAttr *css = sp_repr_css_attr_new ();
3599     sp_repr_css_set_property (css, "font-family", family);
3601     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3602     if (result_numbers == QUERY_STYLE_NOTHING)
3603     {
3604         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3605         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3606     }
3607     else
3608     {
3609         sp_desktop_set_style (desktop, css, true, true);
3610     }
3612     sp_style_unref(query);
3614     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3615                                    _("Text: Change font family"));
3616     sp_repr_css_attr_unref (css);
3617     free (family);
3618     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3620     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3623 void
3624 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3625                                        GObject      *tbl)
3627     const char *family = gtk_entry_get_text (entry);
3629     try {
3630         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3631         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3632         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3633         gtk_tree_selection_select_path (selection, path.gobj());
3634         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3635         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3636     } catch (...) {
3637         if (family && strlen (family))
3638         {
3639             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3640         }
3641     }
3644 void
3645 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3646                                    gpointer          data)
3648     if (g_object_get_data (G_OBJECT (button), "block")) return;
3649     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3650     int prop = GPOINTER_TO_INT(data);
3652     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3653     SPCSSAttr *css = sp_repr_css_attr_new ();
3655     switch (prop)
3656     {
3657         case 0:
3658         {
3659             sp_repr_css_set_property (css, "text-anchor", "start");
3660             sp_repr_css_set_property (css, "text-align", "start");
3661             break;
3662         }
3663         case 1:
3664         {
3665             sp_repr_css_set_property (css, "text-anchor", "middle");
3666             sp_repr_css_set_property (css, "text-align", "center");
3667             break;
3668         }
3670         case 2:
3671         {
3672             sp_repr_css_set_property (css, "text-anchor", "end");
3673             sp_repr_css_set_property (css, "text-align", "end");
3674             break;
3675         }
3677         case 3:
3678         {
3679             sp_repr_css_set_property (css, "text-anchor", "start");
3680             sp_repr_css_set_property (css, "text-align", "justify");
3681             break;
3682         }
3683     }
3685     SPStyle *query =
3686         sp_style_new (SP_ACTIVE_DOCUMENT);
3687     int result_numbers =
3688         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3690     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3691     if (result_numbers == QUERY_STYLE_NOTHING)
3692     {
3693         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3694     }
3696     sp_style_unref(query);
3698     sp_desktop_set_style (desktop, css, true, true);
3699     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3700                                    _("Text: Change alignment"));
3701     sp_repr_css_attr_unref (css);
3703     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3706 void
3707 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
3708                                gpointer          data)
3710     if (g_object_get_data (G_OBJECT (button), "block")) return;
3712     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3713     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3714     int          prop       = GPOINTER_TO_INT(data);
3715     bool         active     = gtk_toggle_button_get_active (button);
3718     switch (prop)
3719     {
3720         case 0:
3721         {
3722             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
3723             break;
3724         }
3726         case 1:
3727         {
3728             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
3729             break;
3730         }
3731     }
3733     SPStyle *query =
3734         sp_style_new (SP_ACTIVE_DOCUMENT);
3735     int result_numbers =
3736         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3738     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3739     if (result_numbers == QUERY_STYLE_NOTHING)
3740     {
3741         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3742     }
3744     sp_style_unref(query);
3746     sp_desktop_set_style (desktop, css, true, true);
3747     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3748                                    _("Text: Change font style"));
3749     sp_repr_css_attr_unref (css);
3751     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3754 void
3755 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
3756                                      gpointer         data)
3758     if (g_object_get_data (G_OBJECT (button), "block")) {
3759         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3760         return;
3761     }
3763     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3764     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3765     int          prop       = GPOINTER_TO_INT(data);
3767     switch (prop)
3768     {
3769         case 0:
3770         {
3771             sp_repr_css_set_property (css, "writing-mode", "lr");
3772             break;
3773         }
3775         case 1:
3776         {
3777             sp_repr_css_set_property (css, "writing-mode", "tb");
3778             break;
3779         }
3780     }
3782     SPStyle *query =
3783         sp_style_new (SP_ACTIVE_DOCUMENT);
3784     int result_numbers =
3785         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3787     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3788     if (result_numbers == QUERY_STYLE_NOTHING)
3789     {
3790         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3791     }
3793     sp_desktop_set_style (desktop, css, true, true);
3794     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3795                                    _("Text: Change orientation"));
3796     sp_repr_css_attr_unref (css);
3798     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3801 gboolean
3802 sp_text_toolbox_size_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
3804     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3805     if (!desktop) return FALSE;
3807     switch (get_group0_keyval (event)) {
3808         case GDK_Escape: // defocus
3809             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3810             return TRUE; // I consumed the event
3811             break;
3812         case GDK_Return: // defocus
3813         case GDK_KP_Enter:
3814             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3815             return TRUE; // I consumed the event
3816             break;
3817     }
3818     return FALSE;
3821 gboolean
3822 sp_text_toolbox_family_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3824     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3825     if (!desktop) return FALSE;
3827     switch (get_group0_keyval (event)) {
3828         case GDK_Escape: // defocus
3829             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3830             sp_text_toolbox_selection_changed (NULL, tbl); // update
3831             return TRUE; // I consumed the event
3832             break;
3833     }
3834     return FALSE;
3837 gboolean
3838 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3840     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3841     if (!desktop) return FALSE;
3843     switch (get_group0_keyval (event)) {
3844         case GDK_Escape: // defocus
3845             gtk_widget_hide (w);
3846             visible = false;
3847             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3848             return TRUE; // I consumed the event
3849             break;
3850     }
3851     return FALSE;
3855 void
3856 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
3857                                GObject     *tbl)
3859     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3861     if (g_object_get_data (tbl, "size-block")) return;
3863 #if GTK_CHECK_VERSION(2,6,0)
3864     char *text = gtk_combo_box_get_active_text (cbox);
3865 #else // GTK_CHECK_VERSION(2,6,0)
3866     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
3867     GtkTreeIter iter;
3868     char *text = NULL;
3870     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
3871         gtk_tree_model_get (model, &iter, 0, &text, -1);
3872 #endif // GTK_CHECK_VERSION(2,6,0)
3874     SPCSSAttr *css = sp_repr_css_attr_new ();
3875     sp_repr_css_set_property (css, "font-size", text);
3876     free (text);
3878     SPStyle *query =
3879         sp_style_new (SP_ACTIVE_DOCUMENT);
3880     int result_numbers =
3881         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3883     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3884     if (result_numbers == QUERY_STYLE_NOTHING)
3885     {
3886         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3887     }
3889     sp_style_unref(query);
3891     sp_desktop_set_style (desktop, css, true, true);
3892     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
3893                                    _("Text: Change font size"));
3894     sp_repr_css_attr_unref (css);
3897     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
3898         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3901 void
3902 sp_text_toolbox_text_popdown_clicked    (GtkButton          *button,
3903                                          GObject            *tbl)
3905     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3906     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3907     int x, y;
3909     if (!visible)
3910     {
3911         gdk_window_get_origin (widget->window, &x, &y);
3912         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
3913         gtk_widget_show_all (popdown);
3915         gdk_pointer_grab (widget->window, TRUE,
3916                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
3917                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
3918                                         GDK_POINTER_MOTION_MASK),
3919                           NULL, NULL, GDK_CURRENT_TIME);
3921         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
3923         visible = true;
3924     }
3925     else
3926     {
3927         gdk_pointer_ungrab (GDK_CURRENT_TIME);
3928         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3929         gtk_widget_hide (popdown);
3930         visible = false;
3931     }
3934 gboolean
3935 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
3936                                  GdkEventFocus    *event,
3937                                  GObject          *tbl)
3939     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
3940     return FALSE;
3943 gboolean
3944 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
3945                                    GdkEventFocus    *event,
3946                                    GObject          *tbl)
3948     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3950     gtk_widget_hide (popdown);
3951     visible = false;
3952     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3953     return TRUE;
3956 void
3957 cell_data_func  (GtkTreeViewColumn *column,
3958                  GtkCellRenderer   *cell,
3959                  GtkTreeModel      *tree_model,
3960                  GtkTreeIter       *iter,
3961                  gpointer           data)
3963     char        *family,
3964         *family_escaped,
3965         *sample_escaped;
3967     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
3969     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
3971     family_escaped = g_markup_escape_text (family, -1);
3972     sample_escaped = g_markup_escape_text (sample, -1);
3974     std::stringstream markup;
3975     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
3976     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
3978     free (family);
3979     free (family_escaped);
3980     free (sample_escaped);
3983 static void delete_completion(GObject *obj, GtkWidget *entry) {
3984     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
3985     if (completion) {
3986         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
3987         g_object_unref (completion);
3988     }
3991 GtkWidget*
3992 sp_text_toolbox_new (SPDesktop *desktop)
3994     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
3996     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
3997     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
3999     GtkTooltips *tt = gtk_tooltips_new();
4000     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4002     ////////////Family
4003     //Window
4004     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4005     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4007     //Entry
4008     GtkWidget           *entry = gtk_entry_new ();
4009     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4010     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4011     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4012     gtk_entry_completion_set_text_column (completion, 0);
4013     gtk_entry_completion_set_minimum_key_length (completion, 1);
4014     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4015     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4016     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4017     aux_toolbox_space (tbl, 1);
4018     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4019     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4021     //Button
4022     GtkWidget   *button = gtk_button_new ();
4023     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4024     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4026     //Popdown
4027     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4028     GtkWidget           *treeview = gtk_tree_view_new ();
4030     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4031     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4032     gtk_tree_view_column_pack_start (column, cell, FALSE);
4033     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4034     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4035     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4037     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4038     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4039 #if GTK_CHECK_VERSION(2,6,0)
4040     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4041 #endif // GTK_CHECK_VERSION(2,6,0)
4043     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4045     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4046     gtk_container_add (GTK_CONTAINER (sw), treeview);
4048     gtk_container_add (GTK_CONTAINER (window), sw);
4049     gtk_widget_set_size_request (window, 300, 450);
4051     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4052     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4053     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4055     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4057     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4058     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4060     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4061     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4063     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4064     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4065     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4066     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4067     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4069     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4070     aux_toolbox_space (tbl, 1);
4071     GtkWidget *box = gtk_event_box_new ();
4072     gtk_container_add (GTK_CONTAINER (box), image);
4073     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4074     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4075     GtkTooltips *tooltips = gtk_tooltips_new ();
4076     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4077     gtk_widget_hide (GTK_WIDGET (box));
4078     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4080     ////////////Size
4081     const char *sizes[] = {
4082         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4083         "16", "18", "20", "22", "24", "28",
4084         "32", "36", "40", "48", "56", "64", "72", "144"
4085     };
4087     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4088     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4089     gtk_widget_set_size_request (cbox, 80, -1);
4090     aux_toolbox_space (tbl, 1);
4091     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4092     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4093     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4094     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4096     //spacer
4097     aux_toolbox_space (tbl, 4);
4098     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4100     ////////////Text anchor
4101     GtkWidget *group   = gtk_radio_button_new (NULL);
4102     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4103     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4105     // left
4106     GtkWidget *rbutton = group;
4107     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4108     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4109     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4111     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4112     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4113     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4114     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4116     // center
4117     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4118     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4119     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4120     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4122     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4123     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4124     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4125     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4127     // right
4128     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4129     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4130     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4131     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4133     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4134     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4135     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4136     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4138     // fill
4139     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4140     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4141     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4142     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4144     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4145     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4146     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4147     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4149     aux_toolbox_space (tbl, 1);
4150     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4152     //spacer
4153     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4155     ////////////Text style
4156     row = gtk_hbox_new (FALSE, 4);
4158     // bold
4159     rbutton = gtk_toggle_button_new ();
4160     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4161     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4162     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4163     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4165     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4166     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4167     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4169     // italic
4170     rbutton = gtk_toggle_button_new ();
4171     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4172     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4173     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4174     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4176     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4177     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4178     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4180     aux_toolbox_space (tbl, 1);
4181     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4183     //spacer
4184     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4186     ////////////Text orientation
4187     group   = gtk_radio_button_new (NULL);
4188     row     = gtk_hbox_new (FALSE, 4);
4189     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4191     // horizontal
4192     rbutton = group;
4193     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4194     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4195     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4196     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4198     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4199     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4200     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4202     // vertical
4203     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4204     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4205     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4206     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4207     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4209     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4210     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4211     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4212     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4215     //watch selection
4216     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4218     sigc::connection *c_selection_changed =
4219         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4220                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4221     pool->add_connection ("selection-changed", c_selection_changed);
4223     sigc::connection *c_selection_modified =
4224         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4225                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4226     pool->add_connection ("selection-modified", c_selection_modified);
4228     sigc::connection *c_subselection_changed =
4229         new sigc::connection (desktop->connectToolSubselectionChanged
4230                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4231     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4233     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4236 #if 0
4237     // horizontal
4238     {
4239         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4240         GtkWidget *b = group = gtk_radio_button_new (NULL);
4241         gtk_container_add (GTK_CONTAINER (b), px);
4242         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4243         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4244         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4245         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4246     }
4248     // vertical
4249     {
4250         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4251         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4252         gtk_container_add (GTK_CONTAINER (b), px);
4253         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4254         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4255         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4256         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4257     }
4259     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4261     // letter spacing
4262     {
4263         {
4264             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4265             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4266             gtk_container_add (GTK_CONTAINER (hb), image);
4267             gtk_widget_show(image);
4268             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4269         }
4271         {
4272             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between letters"),
4273                                              "tools.text", "letter_spacing", 0.0,
4274                                              us, tbl, FALSE, NULL,
4275                                              -1000.0, 1000.0, 0.1, 0.1,
4276                                              sp_text_letter_changed, 0.1, 1);
4277             gtk_widget_set_size_request (hb, 45, 6);
4278             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4279         }
4280     }
4282     // line spacing
4283     {
4284         {
4285             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4286             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4287             gtk_container_add (GTK_CONTAINER (hb), image);
4288             gtk_widget_show(image);
4289             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4290         }
4292         {
4293             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between lines"),
4294                                              "tools.text", "line_spacing", 0,
4295                                              us, tbl, FALSE, NULL,
4296                                              -1000.0, 1000.0, 0.1, 0.1,
4297                                              sp_text_line_changed, 0.1, 1);
4298             gtk_widget_set_size_request (hb, 45, 0);
4299             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4300         }
4301     }
4303     {
4304         // horizontal kerning/vertical kerning units menu: create
4305         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4306         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4307         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4309         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4311         // horizontal kerning
4312         {
4313             {
4314                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4315                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4316                 gtk_container_add (GTK_CONTAINER (hb), image);
4317                 gtk_widget_show(image);
4318                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4319             }
4321             {
4322                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Horizontal kerning"),
4323                                                  "tools.text", "horizontal_kerning", 0,
4324                                                  us, tbl, FALSE, NULL,
4325                                                  -100.00, 100.00, 0.01, 0.1,
4326                                                  sp_text_horiz_kern_changed);
4327                 gtk_widget_set_size_request (hb, 45, 0);
4328                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4329             }
4330         }
4332         // vertical kerning
4333         {
4334             {
4335                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4336                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4337                 gtk_container_add (GTK_CONTAINER (hb), image);
4338                 gtk_widget_show(image);
4339                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4340             }
4342             {
4343                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Vertical kerning"),
4344                                                  "tools.text", "vertical_kerning", 0,
4345                                                  us, tbl, FALSE, NULL,
4346                                                  -100.00, 100.00, 0.01, 0.1,
4347                                                  sp_text_vert_kern_changed);
4348                 gtk_widget_set_size_request (hb, 45, 0);
4349                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4350             }
4351         }
4353         // add the units menu
4354         gtk_widget_show(us);
4355         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4356         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4357     }
4359     // letter rotation
4360     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4361     {
4362         {
4363             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4364             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4365             gtk_container_add (GTK_CONTAINER (hb), image);
4366             gtk_widget_show(image);
4367             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4368         }
4369         {
4370             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Letter rotation"),
4371                                              "tools.text", "letter_rotation", 0,
4372                                              us, tbl, FALSE, NULL,
4373                                              -180.0, 180.0, 0.1, 0.1,
4374                                              sp_text_letter_rotation_changed, 0.1, 1);
4375             gtk_widget_set_size_request (hb, 45, 0);
4376             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4377         }
4378         // rotation degree label
4379         {
4380             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4381             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4382         }
4383     }
4385     // Remove Manual Kerns
4386     {
4387         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4388         GtkWidget *button = gtk_button_new ();
4389         gtk_container_add (GTK_CONTAINER (button), px);
4390         gtk_widget_show(button);
4391         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4392         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4393         gtk_widget_set_sensitive(button, TRUE);
4394         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4395     }
4396 #endif
4398     gtk_widget_show_all (tbl);
4399     return tbl;
4401 } // end of sp_text_toolbox_new()
4403 }//<unnamed> namespace
4406 //#########################
4407 //##  Connector Toolbox  ##
4408 //#########################
4410 static void sp_connector_path_set_avoid(void)
4412     cc_selection_set_avoid(true);
4416 static void sp_connector_path_set_ignore(void)
4418     cc_selection_set_avoid(false);
4423 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4425     // quit if run by the _changed callbacks
4426     if (g_object_get_data( tbl, "freeze" )) {
4427         return;
4428     }
4430     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4431     SPDocument *doc = sp_desktop_document(desktop);
4433     if (!sp_document_get_undo_sensitive(doc))
4434     {
4435         return;
4436     }
4438     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4440     if ( repr->attribute("inkscape:connector-spacing") ) {
4441         gdouble priorValue = gtk_adjustment_get_value(adj);
4442         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4443         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4444             return;
4445         }
4446     } else if ( adj->value == defaultConnSpacing ) {
4447         return;
4448     }
4450     // in turn, prevent callbacks from responding
4451     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4453     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4454     SP_OBJECT(desktop->namedview)->updateRepr();
4456     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4457     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4458         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4459         NR::Matrix m = NR::identity();
4460         avoid_item_move(&m, item);
4461     }
4463     if (items) {
4464         g_slist_free(items);
4465     }
4467     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4468             _("Change connector spacing"));
4470     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4472     spinbutton_defocus(GTK_OBJECT(tbl));
4475 static void sp_connector_graph_layout(void)
4477     if (!SP_ACTIVE_DESKTOP) return;
4479     // hack for clones, see comment in align-and-distribute.cpp
4480     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4481     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4483     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4485     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4487     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4490 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4492     if ( gtk_toggle_action_get_active( act ) ) {
4493         prefs_set_string_attribute("tools.connector", "directedlayout",
4494                 "true");
4495     } else {
4496         prefs_set_string_attribute("tools.connector", "directedlayout",
4497                 "false");
4498     }
4501 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4503     if ( gtk_toggle_action_get_active( act ) ) {
4504         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4505                 "true");
4506     } else {
4507         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4508                 "false");
4509     }
4513 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4515     prefs_set_double_attribute("tools.connector", "length", adj->value);
4516     spinbutton_defocus(GTK_OBJECT(tbl));
4519 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4520         gchar const *name, gchar const *old_value, gchar const *new_value,
4521         bool is_interactive, gpointer data)
4523     GtkWidget *tbl = GTK_WIDGET(data);
4525     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4526         return;
4527     }
4528     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4529         return;
4530     }
4532     GtkAdjustment *adj = (GtkAdjustment*)
4533             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4534     gdouble spacing = defaultConnSpacing;
4535     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4537     gtk_adjustment_set_value(adj, spacing);
4541 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4542     NULL, /* child_added */
4543     NULL, /* child_removed */
4544     connector_tb_event_attr_changed,
4545     NULL, /* content_changed */
4546     NULL  /* order_changed */
4547 };
4550 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4552     {
4553         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4554                                           _("Avoid"),
4555                                           _("Make connectors avoid selected objects"),
4556                                           "connector_avoid",
4557                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4558         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
4559         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4560     }
4562     {
4563         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
4564                                           _("Ignore"),
4565                                           _("Make connectors ignore selected objects"),
4566                                           "connector_ignore",
4567                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4568         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
4569         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4570     }
4572     EgeAdjustmentAction* eact = 0;
4574     // Spacing spinbox
4575     eact = create_adjustment_action( "ConnectorSpacingAction",
4576                                      _("Spacing:"), _("The amount of space left around objects by auto-routing connectors"),
4577                                      "tools.connector", "spacing", defaultConnSpacing,
4578                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
4579                                      0, 100, 1.0, 10.0,
4580                                      0, 0, 0,
4581                                      connector_spacing_changed, 1, 0 );
4582     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4584     // Graph (connector network) layout
4585     {
4586         InkAction* inky = ink_action_new( "ConnectorGraphAction",
4587                                           _("Graph"),
4588                                           _("Nicely arrange selected connector network"),
4589                                           "graph_layout",
4590                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4591         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
4592         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4593     }
4595     // Default connector length spinbox
4596     eact = create_adjustment_action( "ConnectorLengthAction",
4597                                      _("Length:"), _("Ideal length for connectors when layout is applied"),
4598                                      "tools.connector", "length", 100,
4599                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
4600                                      10, 1000, 10.0, 100.0,
4601                                      0, 0, 0,
4602                                      connector_length_changed, 1, 0 );
4603     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4606     // Directed edges toggle button
4607     {
4608         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
4609                                                       _("Downwards"),
4610                                                       _("Make connectors with end-markers (arrows) point downwards"),
4611                                                       "directed_graph",
4612                                                       Inkscape::ICON_SIZE_DECORATION );
4613         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4615         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
4616         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4617                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4619         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
4620     }
4622     // Avoid overlaps toggle button
4623     {
4624         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
4625                                                       _("Remove overlaps"),
4626                                                       _("Do not allow overlapping shapes"),
4627                                                       "remove_overlaps",
4628                                                       Inkscape::ICON_SIZE_DECORATION );
4629         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4631         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
4632         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4633                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4635         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
4636     }
4638     // Code to watch for changes to the connector-spacing attribute in
4639     // the XML.
4640     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4641     g_assert(repr != NULL);
4643     purge_repr_listener( holder, holder );
4645     if (repr) {
4646         g_object_set_data( holder, "repr", repr );
4647         Inkscape::GC::anchor(repr);
4648         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
4649         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
4650     }
4651 } // end of sp_connector_toolbox_prep()
4653 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* tbl)
4655     gint channels = ege_select_one_action_get_active( act );
4656     flood_channels_set_channels( channels );
4659 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject *tbl)
4661     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4664 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject *tbl)
4666     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
4669 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
4671     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
4672     SPUnit const *unit = tracker->getActiveUnit();
4675     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4678 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
4680     // FIXME: make defaults settable via Inkscape Options
4681     struct KeyValue {
4682         char const *key;
4683         double value;
4684     } const key_values[] = {
4685         {"threshold", 15},
4686         {"offset", 0.0}
4687     };
4689     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
4690         KeyValue const &kv = key_values[i];
4691         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
4692         if ( adj ) {
4693             gtk_adjustment_set_value(adj, kv.value);
4694         }
4695     }
4696     
4697     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
4698     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
4699     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
4700     ege_select_one_action_set_active( autogap_action, 0 );
4703 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4705     EgeAdjustmentAction* eact = 0;
4707     {
4708         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4710         GList* items = 0;
4711         gint count = 0;
4712         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
4713         {
4714             GtkTreeIter iter;
4715             gtk_list_store_append( model, &iter );
4716             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4717             count++;
4718         }
4719         g_list_free( items );
4720         items = 0;
4721         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by:"), _(""), NULL, GTK_TREE_MODEL(model) );
4722         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
4723         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
4724         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4725         g_object_set_data( holder, "channels_action", act1 );
4726     }
4728     // Spacing spinbox
4729     {
4730         eact = create_adjustment_action(
4731             "ThresholdAction",
4732             _("Threshold:"),
4733             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
4734             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
4735             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
4736             0, 0, 0,
4737             paintbucket_threshold_changed, 1, 0 );
4739         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4740     }
4742     // Create the units menu.
4743     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
4744     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
4745     g_object_set_data( holder, "tracker", tracker );
4746     {
4747         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), _("") );
4748         gtk_action_group_add_action( mainActions, act );
4749     }
4751     // Offset spinbox
4752     {
4753         eact = create_adjustment_action(
4754             "OffsetAction",
4755             _("Grow/shrink by:"),
4756             _("The amount to grow (positive) or shrink (negative) the created fill path"),
4757             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
4758             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
4759             0, 0, 0,
4760             paintbucket_offset_changed, 1, 2);
4761         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4763         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4764     }
4765     
4766     /* Auto Gap */
4767     {
4768         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4770         GList* items = 0;
4771         gint count = 0;
4772         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
4773         {
4774             GtkTreeIter iter;
4775             gtk_list_store_append( model, &iter );
4776             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4777             count++;
4778         }
4779         g_list_free( items );
4780         items = 0;
4781         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Fill gaps:"), _(""), NULL, GTK_TREE_MODEL(model) );
4782         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
4783         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
4784         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
4785         g_object_set_data( holder, "autogap_action", act2 );
4786     }
4788     /* Reset */
4789     {
4790         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
4791                                           _("Defaults"),
4792                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4793                                           GTK_STOCK_CLEAR );
4794         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
4795         gtk_action_group_add_action( mainActions, act );
4796         gtk_action_set_sensitive( act, TRUE );
4797     }
4801 /*
4802   Local Variables:
4803   mode:c++
4804   c-file-style:"stroustrup"
4805   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4806   indent-tabs-mode:nil
4807   fill-column:99
4808   End:
4809 */
4810 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :