Code

Added preference to allow for system toolbox size
[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 "nodepath.h"
51 #include "xml/repr.h"
52 #include "xml/node-event-vector.h"
53 #include <glibmm/i18n.h>
54 #include "helper/unit-menu.h"
55 #include "helper/units.h"
57 #include "inkscape.h"
58 #include "conn-avoid-ref.h"
61 #include "select-toolbar.h"
62 #include "gradient-toolbar.h"
64 #include "connector-context.h"
65 #include "sp-rect.h"
66 #include "sp-star.h"
67 #include "sp-spiral.h"
68 #include "sp-ellipse.h"
69 #include "sp-text.h"
70 #include "sp-flowtext.h"
71 #include "style.h"
72 #include "selection.h"
73 #include "document-private.h"
74 #include "desktop-style.h"
75 #include "../libnrtype/font-lister.h"
76 #include "../connection-pool.h"
77 #include "../prefs-utils.h"
78 #include "../inkscape-stock.h"
79 #include "icon.h"
80 #include "graphlayout/graphlayout.h"
82 #include "mod360.h"
84 #include "toolbox.h"
86 #include "flood-context.h"
88 #include "ink-action.h"
89 #include "ege-adjustment-action.h"
90 #include "ege-output-action.h"
91 #include "ege-select-one-action.h"
92 #include "helper/unit-tracker.h"
94 using Inkscape::UnitTracker;
96 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
97 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
99 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
100 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
101 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
102 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
103 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
110 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
116 static struct {
117     gchar const *type_name;
118     gchar const *data_name;
119     sp_verb_t verb;
120     sp_verb_t doubleclick_verb;
121 } const tools[] = {
122     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
123     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
124     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
125     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
126     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
127     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
128     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
129     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
130     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
131     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
132     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
133     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
134     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
135     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
136     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
137     { NULL, NULL, 0, 0 }
138 };
140 static struct {
141     gchar const *type_name;
142     gchar const *data_name;
143     GtkWidget *(*create_func)(SPDesktop *desktop);
144     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
145     gchar const *ui_name;
146     gint swatch_verb_id;
147     gchar const *swatch_tool;
148     gchar const *swatch_tip;
149 } const aux_toolboxes[] = {
150     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
151       SP_VERB_INVALID, 0, 0},
152     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
153       SP_VERB_INVALID, 0, 0},
154     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
155       SP_VERB_INVALID, 0, 0},
156     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
157       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
158     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
159       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
160     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
161       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
162     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
163       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
164     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
165       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
166     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
167       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
168     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
169       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
170     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
171       SP_VERB_INVALID, 0, 0},
172     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
173       SP_VERB_INVALID, 0, 0},
174     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
175       SP_VERB_INVALID, 0, 0},
176     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
177       SP_VERB_INVALID, 0, 0},
178     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
179       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
180     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
181 };
184 static gchar const * ui_descr =
185         "<ui>"
186         "  <toolbar name='SelectToolbar'>"
187         "    <toolitem action='ObjectRotate90CCW' />"
188         "    <toolitem action='ObjectRotate90' />"
189         "    <toolitem action='ObjectFlipHorizontally' />"
190         "    <toolitem action='ObjectFlipVertically' />"
191         "    <separator />"
192         "    <toolitem action='SelectionToBack' />"
193         "    <toolitem action='SelectionLower' />"
194         "    <toolitem action='SelectionRaise' />"
195         "    <toolitem action='SelectionToFront' />"
196         "    <separator />"
197         "    <toolitem action='XAction' />"
198         "    <toolitem action='YAction' />"
199         "    <toolitem action='WidthAction' />"
200         "    <toolitem action='LockAction' />"
201         "    <toolitem action='HeightAction' />"
202         "    <toolitem action='UnitsAction' />"
203         "    <separator />"
204         "    <toolitem action='transform_stroke' />"
205         "    <toolitem action='transform_corners' />"
206         "    <toolitem action='transform_gradient' />"
207         "    <toolitem action='transform_pattern' />"
208         "  </toolbar>"
210         "  <toolbar name='NodeToolbar'>"
211         "    <toolitem action='NodeInsertAction' />"
212         "    <toolitem action='NodeDeleteAction' />"
213         "    <separator />"
214         "    <toolitem action='NodeJoinAction' />"
215         "    <toolitem action='NodeJoinSegmentAction' />"
216         "    <toolitem action='NodeDeleteSegmentAction' />"
217         "    <toolitem action='NodeBreakAction' />"
218         "    <separator />"
219         "    <toolitem action='NodeCuspAction' />"
220         "    <toolitem action='NodeSmoothAction' />"
221         "    <toolitem action='NodeSymmetricAction' />"
222         "    <separator />"
223         "    <toolitem action='NodeLineAction' />"
224         "    <toolitem action='NodeCurveAction' />"
225         "    <separator />"
226         "    <toolitem action='ObjectToPath' />"
227         "    <toolitem action='StrokeToPath' />"
228         "    <separator />"
229         "    <toolitem action='NodesShowHandlesAction' />"
230         "  </toolbar>"
232         "  <toolbar name='ZoomToolbar'>"
233         "    <toolitem action='ZoomIn' />"
234         "    <toolitem action='ZoomOut' />"
235         "    <separator />"
236         "    <toolitem action='ZoomSelection' />"
237         "    <toolitem action='ZoomDrawing' />"
238         "    <toolitem action='ZoomPage' />"
239         "    <toolitem action='ZoomPageWidth' />"
240         "    <separator />"
241         "    <toolitem action='ZoomPrev' />"
242         "    <toolitem action='ZoomNext' />"
243         "    <separator />"
244         "    <toolitem action='Zoom1:0' />"
245         "    <toolitem action='Zoom1:2' />"
246         "    <toolitem action='Zoom2:1' />"
247         "  </toolbar>"
249         "  <toolbar name='StarToolbar'>"
250         "    <separator />"
251         "    <toolitem action='StarStateAction' />"
252         "    <separator />"
253         "    <toolitem action='FlatAction' />"
254         "    <separator />"
255         "    <toolitem action='MagnitudeAction' />"
256         "    <toolitem action='SpokeAction' />"
257         "    <toolitem action='RoundednessAction' />"
258         "    <toolitem action='RandomizationAction' />"
259         "    <separator />"
260         "    <toolitem action='StarResetAction' />"
261         "  </toolbar>"
263         "  <toolbar name='RectToolbar'>"
264         "    <toolitem action='RectStateAction' />"
265         "    <toolitem action='RectWidthAction' />"
266         "    <toolitem action='RectHeightAction' />"
267         "    <toolitem action='RadiusXAction' />"
268         "    <toolitem action='RadiusYAction' />"
269         "    <toolitem action='RectUnitsAction' />"
270         "    <separator />"
271         "    <toolitem action='RectResetAction' />"
272         "  </toolbar>"
274         "  <toolbar name='SpiralToolbar'>"
275         "    <toolitem action='SpiralStateAction' />"
276         "    <toolitem action='SpiralRevolutionAction' />"
277         "    <toolitem action='SpiralExpansionAction' />"
278         "    <toolitem action='SpiralT0Action' />"
279         "    <separator />"
280         "    <toolitem action='SpiralResetAction' />"
281         "  </toolbar>"
283         "  <toolbar name='PenToolbar'>"
284         "  </toolbar>"
286         "  <toolbar name='PencilToolbar'>"
287         "  </toolbar>"
289         "  <toolbar name='CalligraphyToolbar'>"
290         "    <separator />"
291         "    <toolitem action='CalligraphyWidthAction' />"
292         "    <toolitem action='ThinningAction' />"
293         "    <separator />"
294         "    <toolitem action='AngleAction' />"
295         "    <toolitem action='FixationAction' />"
296         "    <toolitem action='CapRoundingAction' />"
297         "    <separator />"
298         "    <toolitem action='TremorAction' />"
299         "    <toolitem action='WiggleAction' />"
300         "    <toolitem action='MassAction' />"
301         "    <separator />"
302         "    <toolitem action='TraceAction' />"
303         "    <toolitem action='PressureAction' />"
304         "    <toolitem action='TiltAction' />"
305         "    <toolitem action='CalligraphyResetAction' />"
306         "  </toolbar>"
308         "  <toolbar name='ArcToolbar'>"
309         "    <toolitem action='ArcStateAction' />"
310         "    <separator />"
311         "    <toolitem action='ArcStartAction' />"
312         "    <toolitem action='ArcEndAction' />"
313         "    <separator />"
314         "    <toolitem action='ArcOpenAction' />"
315         "    <separator />"
316         "    <toolitem action='ArcResetAction' />"
317         "    <separator />"
318         "  </toolbar>"
320         "  <toolbar name='PaintbucketToolbar'>"
321         "    <toolitem action='ChannelsAction' />"
322         "    <separator />"
323         "    <toolitem action='ThresholdAction' />"
324         "    <separator />"
325         "    <toolitem action='OffsetAction' />"
326         "    <toolitem action='PaintbucketUnitsAction' />"
327         "    <separator />"
328         "    <toolitem action='PaintbucketResetAction' />"
329         "  </toolbar>"
331         "  <toolbar name='DropperToolbar'>"
332         "    <toolitem action='DropperPickAlphaAction' />"
333         "    <toolitem action='DropperSetAlphaAction' />"
334         "  </toolbar>"
336         "  <toolbar name='ConnectorToolbar'>"
337         "    <toolitem action='ConnectorAvoidAction' />"
338         "    <toolitem action='ConnectorIgnoreAction' />"
339         "    <toolitem action='ConnectorSpacingAction' />"
340         "    <toolitem action='ConnectorGraphAction' />"
341         "    <toolitem action='ConnectorLengthAction' />"
342         "    <toolitem action='ConnectorDirectedAction' />"
343         "    <toolitem action='ConnectorOverlapAction' />"
344         "  </toolbar>"
346         "</ui>"
349 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
351 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
353 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
354 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
356 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
357 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
359 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
360 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
362 /* Global text entry widgets necessary for update */
363 /* GtkWidget *dropper_rgb_entry,
364           *dropper_opacity_entry ; */
365 // should be made a private member once this is converted to class
367 static void delete_connection(GObject *obj, sigc::connection *connection) {
368     connection->disconnect();
369     delete connection;
372 static void purge_repr_listener( GObject* obj, GObject* tbl )
374     (void)obj;
375     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
376     if (oldrepr) { // remove old listener
377         sp_repr_remove_listener_by_data(oldrepr, tbl);
378         Inkscape::GC::release(oldrepr);
379         oldrepr = 0;
380         g_object_set_data( tbl, "repr", NULL );
381     }
384 GtkWidget *
385 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
386                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
387                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
389     SPAction *action = verb->get_action(view);
390     if (!action) return NULL;
392     SPAction *doubleclick_action;
393     if (doubleclick_verb)
394         doubleclick_action = doubleclick_verb->get_action(view);
395     else
396         doubleclick_action = NULL;
398     /* fixme: Handle sensitive/unsensitive */
399     /* fixme: Implement sp_button_new_from_action */
400     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
401     gtk_widget_show(b);
402     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
404     return b;
407 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
408                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
410     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
413 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
414                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
416     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
420 static void trigger_sp_action( GtkAction* act, gpointer user_data )
422     SPAction* targetAction = SP_ACTION(user_data);
423     if ( targetAction ) {
424         sp_action_perform( targetAction, NULL );
425     }
428 static void sp_action_action_set_sensitive (SPAction *action, unsigned int sensitive, void *data)
430     if ( data ) {
431         GtkAction* act = GTK_ACTION(data);
432         gtk_action_set_sensitive( act, sensitive );
433     }
436 static SPActionEventVector action_event_vector = {
437     {NULL},
438     NULL,
439     NULL,
440     sp_action_action_set_sensitive,
441     NULL,
442     NULL
443 };
445 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
447     GtkAction* act = 0;
449     SPAction* targetAction = verb->get_action(view);
450     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
451     act = GTK_ACTION(inky);
452     gtk_action_set_sensitive( act, targetAction->sensitive );
454     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
456     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
457     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
459     return act;
462 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
464     Inkscape::UI::View::View *view = desktop;
465     gint verbsToUse[] = {
466         // disabled until we have icons for them:
467         //find
468         //SP_VERB_EDIT_TILE,
469         //SP_VERB_EDIT_UNTILE,
470         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
471         SP_VERB_DIALOG_DISPLAY,
472         SP_VERB_DIALOG_FILL_STROKE,
473         SP_VERB_DIALOG_NAMEDVIEW,
474         SP_VERB_DIALOG_TEXT,
475         SP_VERB_DIALOG_XML_EDITOR,
476         SP_VERB_EDIT_CLONE,
477         SP_VERB_EDIT_COPY,
478         SP_VERB_EDIT_CUT,
479         SP_VERB_EDIT_DUPLICATE,
480         SP_VERB_EDIT_PASTE,
481         SP_VERB_EDIT_REDO,
482         SP_VERB_EDIT_UNDO,
483         SP_VERB_EDIT_UNLINK_CLONE,
484         SP_VERB_FILE_EXPORT,
485         SP_VERB_FILE_IMPORT,
486         SP_VERB_FILE_NEW,
487         SP_VERB_FILE_OPEN,
488         SP_VERB_FILE_PRINT,
489         SP_VERB_FILE_SAVE,
490         SP_VERB_OBJECT_TO_CURVE,
491         SP_VERB_SELECTION_GROUP,
492         SP_VERB_SELECTION_OUTLINE,
493         SP_VERB_SELECTION_UNGROUP,
494         SP_VERB_ZOOM_1_1,
495         SP_VERB_ZOOM_1_2,
496         SP_VERB_ZOOM_2_1,
497         SP_VERB_ZOOM_DRAWING,
498         SP_VERB_ZOOM_IN,
499         SP_VERB_ZOOM_NEXT,
500         SP_VERB_ZOOM_OUT,
501         SP_VERB_ZOOM_PAGE,
502         SP_VERB_ZOOM_PAGE_WIDTH,
503         SP_VERB_ZOOM_PREV,
504         SP_VERB_ZOOM_SELECTION,
505     };
507     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
508     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
510     static std::map<SPDesktop*, GtkActionGroup*> groups;
511     GtkActionGroup* mainActions = 0;
512     if ( groups.find(desktop) != groups.end() ) {
513         mainActions = groups[desktop];
514     }
516     if ( !mainActions ) {
517         mainActions = gtk_action_group_new("main");
518         groups[desktop] = mainActions;
519     }
521     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
522         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
523         if ( verb ) {
524             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
525                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
526                 gtk_action_group_add_action( mainActions, act );
527             }
528         }
529     }
531     return mainActions;
535 GtkWidget *
536 sp_tool_toolbox_new()
538     GtkTooltips *tt = gtk_tooltips_new();
539     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
541     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
542     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
544     gtk_widget_set_sensitive(tb, FALSE);
546     GtkWidget *hb = gtk_handle_box_new();
547     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
548     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
549     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
551     gtk_container_add(GTK_CONTAINER(hb), tb);
552     gtk_widget_show(GTK_WIDGET(tb));
554     sigc::connection* conn = new sigc::connection;
555     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
557     return hb;
560 static void
561 aux_toolbox_attached(GtkHandleBox *toolbox, GtkWidget *child)
563     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
564     gtk_widget_queue_resize(child);
567 static void
568 aux_toolbox_detached(GtkHandleBox *toolbox, GtkWidget *child)
570     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
571     gtk_widget_queue_resize(child);
574 GtkWidget *
575 sp_aux_toolbox_new()
577     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
579     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
580     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
581     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
582     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
583     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
585     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
586     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
588     gtk_widget_set_sensitive(tb, FALSE);
590     GtkWidget *hb = gtk_handle_box_new();
591     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
592     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
593     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
595     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
596     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
598     gtk_container_add(GTK_CONTAINER(hb), tb);
599     gtk_widget_show(GTK_WIDGET(tb));
601     sigc::connection* conn = new sigc::connection;
602     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
604     return hb;
607 //####################################
608 //# Commands Bar
609 //####################################
611 GtkWidget *
612 sp_commands_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     gtk_widget_set_sensitive(tb, FALSE);
625     GtkWidget *hb = gtk_handle_box_new();
626     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
627     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
628     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
630     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
631     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
633     gtk_container_add(GTK_CONTAINER(hb), tb);
634     gtk_widget_show(GTK_WIDGET(tb));
636     sigc::connection* conn = new sigc::connection;
637     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
639     return hb;
643 //####################################
644 //# node editing callbacks
645 //####################################
647 void
648 sp_node_path_edit_add(void)
650     sp_node_selected_add_node();
653 void
654 sp_node_path_edit_delete(void)
656     sp_node_selected_delete();
659 void
660 sp_node_path_edit_delete_segment(void)
662     sp_node_selected_delete_segment();
665 void
666 sp_node_path_edit_break(void)
668     sp_node_selected_break();
671 void
672 sp_node_path_edit_join(void)
674     sp_node_selected_join();
677 void
678 sp_node_path_edit_join_segment(void)
680     sp_node_selected_join_segment();
683 void
684 sp_node_path_edit_toline(void)
686     sp_node_selected_set_line_type(NR_LINETO);
689 void
690 sp_node_path_edit_tocurve(void)
692     sp_node_selected_set_line_type(NR_CURVETO);
695 void
696 sp_node_path_edit_cusp(void)
698     sp_node_selected_set_type(Inkscape::NodePath::NODE_CUSP);
701 void
702 sp_node_path_edit_smooth(void)
704     sp_node_selected_set_type(Inkscape::NodePath::NODE_SMOOTH);
707 void
708 sp_node_path_edit_symmetrical(void)
710     sp_node_selected_set_type(Inkscape::NodePath::NODE_SYMM);
713 static void toggle_show_handles (GtkToggleAction *act, gpointer data) {
714     bool show = gtk_toggle_action_get_active( act );
715     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
716     sp_nodepath_show_handles(show);
719 //################################
720 //##    Node Editing Toolbox    ##
721 //################################
723 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
725     {
726         InkAction* inky = ink_action_new( "NodeInsertAction",
727                                           _("Insert"),
728                                           _("Insert new nodes into selected segments"),
729                                           "node_insert",
730                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
731         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
732         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
733     }
735     {
736         InkAction* inky = ink_action_new( "NodeDeleteAction",
737                                           _("Delete"),
738                                           _("Delete selected nodes"),
739                                           "node_delete",
740                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
741         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
742         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
743     }
745     {
746         InkAction* inky = ink_action_new( "NodeJoinAction",
747                                           _("Join"),
748                                           _("Join selected endnodes"),
749                                           "node_join",
750                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
751         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
752         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
753     }
755     {
756         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
757                                           _("Join Segment"),
758                                           _("Join selected endnodes with a new segment"),
759                                           "node_join_segment",
760                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
761         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
762         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
763     }
765     {
766         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
767                                           _("Delete Segment"),
768                                           _("Split path between two non-endpoint nodes"),
769                                           "node_delete_segment",
770                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
771         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
772         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
773     }
775     {
776         InkAction* inky = ink_action_new( "NodeBreakAction",
777                                           _("Node Break"),
778                                           _("Break path at selected nodes"),
779                                           "node_break",
780                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
781         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
782         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
783     }
785     {
786         InkAction* inky = ink_action_new( "NodeCuspAction",
787                                           _("Node Cusp"),
788                                           _("Make selected nodes corner"),
789                                           "node_cusp",
790                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
791         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
792         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
793     }
795     {
796         InkAction* inky = ink_action_new( "NodeSmoothAction",
797                                           _("Node Smooth"),
798                                           _("Make selected nodes smooth"),
799                                           "node_smooth",
800                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
801         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
802         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
803     }
805     {
806         InkAction* inky = ink_action_new( "NodeSymmetricAction",
807                                           _("Node Symmetric"),
808                                           _("Make selected nodes symmetric"),
809                                           "node_symmetric",
810                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
811         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
812         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
813     }
815     {
816         InkAction* inky = ink_action_new( "NodeLineAction",
817                                           _("Node Line"),
818                                           _("Make selected segments lines"),
819                                           "node_line",
820                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
821         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
822         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
823     }
825     {
826         InkAction* inky = ink_action_new( "NodeCurveAction",
827                                           _("Node Curve"),
828                                           _("Make selected segments curves"),
829                                           "node_curve",
830                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
831         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
832         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
833     }
835     {
836         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
837                                                       _("Show Handles"),
838                                                       _("Show the Bezier handles of selected nodes"),
839                                                       "nodes_show_handles",
840                                                       Inkscape::ICON_SIZE_DECORATION );
841         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
842         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
843         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
844     }
846 } // end of sp_node_toolbox_prep()
849 //########################
850 //##    Zoom Toolbox    ##
851 //########################
853 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
855     // no custom GtkAction setup needed
856 } // end of sp_zoom_toolbox_prep()
858 void
859 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
861     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")));
865 void
866 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
868     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")));
871 void
872 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
874     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")));
877 static void
878 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
880     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
881     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
883     if (old_desktop) {
884         GList *children, *iter;
886         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
887         for ( iter = children ; iter ; iter = iter->next ) {
888             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
889         }
890         g_list_free(children);
891     }
893     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
895     if (desktop) {
896         gtk_widget_set_sensitive(toolbox, TRUE);
897         setup_func(toolbox, desktop);
898         update_func(desktop, desktop->event_context, toolbox);
899         *conn = desktop->connectEventContextChanged
900             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
901     } else {
902         gtk_widget_set_sensitive(toolbox, FALSE);
903     }
905 } // end of toolbox_set_desktop()
908 static void
909 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
911     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
912     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
913     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
914         // "toolbox.tools" was not set. Fallback to older value
915         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
917         // Copy the setting forwards
918         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
919     }
920     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
922     for (int i = 0 ; tools[i].type_name ; i++ ) {
923         GtkWidget *button =
924             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
925                                                               SP_BUTTON_TYPE_TOGGLE,
926                                                               Inkscape::Verb::get(tools[i].verb),
927                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
928                                                               desktop,
929                                                               tooltips );
931         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
932                            (gpointer)button );
933     }
937 static void
938 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
940     gchar const *const tname = ( eventcontext
941                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
942                                  : NULL );
943     for (int i = 0 ; tools[i].type_name ; i++ ) {
944         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
945         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
946     }
949 static void
950 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
952     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
953     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
954     GtkUIManager* mgr = gtk_ui_manager_new();
955     GError* errVal = 0;
956     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
957     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
959     std::map<std::string, GtkWidget*> dataHolders;
961     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
962         if ( aux_toolboxes[i].prep_func ) {
963             // converted to GtkActions and UIManager
965             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
966             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
967             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
968             dataHolders[aux_toolboxes[i].type_name] = kludge;
969             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
970         } else {
972             GtkWidget *sub_toolbox = 0;
973             if (aux_toolboxes[i].create_func == NULL)
974                 sub_toolbox = sp_empty_toolbox_new(desktop);
975             else {
976                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
977             }
979             gtk_size_group_add_widget( grouper, sub_toolbox );
981             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
982             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
984         }
985     }
987     // Second pass to create toolbars *after* all GtkActions are created
988     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
989         if ( aux_toolboxes[i].prep_func ) {
990             // converted to GtkActions and UIManager
992             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
994             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
995             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
997             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
998             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
999             g_free( tmp );
1000             tmp = 0;
1002             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1003             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1004             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1005                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1006             }
1007             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1010             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1012             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1013                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1014                 swatch->setDesktop( desktop );
1015                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1016                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1017                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1018                 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 );
1019             }
1021             gtk_widget_show_all( holder );
1022             sp_set_font_size_smaller( holder );
1024             gtk_size_group_add_widget( grouper, holder );
1026             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1027             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1028         }
1029     }
1031     g_object_unref( G_OBJECT(grouper) );
1034 static void
1035 update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1037     gchar const *tname = ( eventcontext
1038                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1039                            : NULL );
1040     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1041         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1042         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1043             gtk_widget_show_all(sub_toolbox);
1044             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1045         } else {
1046             gtk_widget_hide(sub_toolbox);
1047         }
1048     }
1051 static void
1052 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1054     gchar const * descr =
1055         "<ui>"
1056         "  <toolbar name='CommandsToolbar'>"
1057         "    <toolitem action='FileNew' />"
1058         "    <toolitem action='FileOpen' />"
1059         "    <toolitem action='FileSave' />"
1060         "    <toolitem action='FilePrint' />"
1061         "    <separator />"
1062         "    <toolitem action='FileImport' />"
1063         "    <toolitem action='FileExport' />"
1064         "    <separator />"
1065         "    <toolitem action='EditUndo' />"
1066         "    <toolitem action='EditRedo' />"
1067         "    <separator />"
1068         "    <toolitem action='EditCopy' />"
1069         "    <toolitem action='EditCut' />"
1070         "    <toolitem action='EditPaste' />"
1071         "    <separator />"
1072         "    <toolitem action='ZoomSelection' />"
1073         "    <toolitem action='ZoomDrawing' />"
1074         "    <toolitem action='ZoomPage' />"
1075         "    <separator />"
1076         "    <toolitem action='EditDuplicate' />"
1077         "    <toolitem action='EditClone' />"
1078         "    <toolitem action='EditUnlinkClone' />"
1079         "    <separator />"
1080         "    <toolitem action='SelectionGroup' />"
1081         "    <toolitem action='SelectionUnGroup' />"
1082         "    <separator />"
1083         "    <toolitem action='DialogFillStroke' />"
1084         "    <toolitem action='DialogText' />"
1085         "    <toolitem action='DialogXMLEditor' />"
1086         "    <toolitem action='DialogAlignDistribute' />"
1087         "    <separator />"
1088         "    <toolitem action='DialogPreferences' />"
1089         "    <toolitem action='DialogDocumentProperties' />"
1090         "  </toolbar>"
1091         "</ui>";
1092     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1095     GtkUIManager* mgr = gtk_ui_manager_new();
1096     GError* errVal = 0;
1098     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1099     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1101     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1102     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1103         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1104     }
1105     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1106     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1107     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1110     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1113 static void
1114 update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1118 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1120     gtk_widget_show(toolbox_toplevel);
1121     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1123     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1124     if (!shown_toolbox) {
1125         return;
1126     }
1127     gtk_widget_show(toolbox);
1129     // need to show the spacer, or the padding will be off
1130     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1131     gtk_widget_show(spacer);
1133     gtk_widget_show_all(shown_toolbox);
1136 void
1137 aux_toolbox_space(GtkWidget *tb, gint space)
1139     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1142 static GtkWidget *
1143 sp_empty_toolbox_new(SPDesktop *desktop)
1145     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1146     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1147     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1149     gtk_widget_show_all(tbl);
1150     sp_set_font_size_smaller (tbl);
1152     return tbl;
1155 // helper UI functions
1157 GtkWidget *
1158 sp_tb_spinbutton(
1159     gchar *label, gchar const *tooltip,
1160     gchar const *path, gchar const *data, gdouble def,
1161     GtkWidget *us,
1162     GtkWidget *tbl,
1163     gboolean altx, gchar const *altx_mark,
1164     gdouble lower, gdouble upper, gdouble step, gdouble page,
1165     void (*callback)(GtkAdjustment *, GtkWidget *),
1166     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1168     GtkTooltips *tt = gtk_tooltips_new();
1170     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1172     GtkWidget *l = gtk_label_new(label);
1173     gtk_widget_show(l);
1174     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1175     gtk_container_add(GTK_CONTAINER(hb), l);
1177     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1178                                       lower, upper, step, page, page);
1179     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1180     if (us)
1181         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1183     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1184     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1185     if (altx)
1186         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1187     gtk_widget_set_size_request(sb,
1188                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1189                                 AUX_SPINBUTTON_HEIGHT);
1190     gtk_widget_show(sb);
1191     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1192     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1193     gtk_container_add(GTK_CONTAINER(hb), sb);
1194     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1196     return hb;
1199 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1200                                                        gchar const *label, gchar const *tooltip,
1201                                                        gchar const *path, gchar const *data, gdouble def,
1202                                                        GtkWidget *focusTarget,
1203                                                        GtkWidget *us,
1204                                                        GObject *dataKludge,
1205                                                        gboolean altx, gchar const *altx_mark,
1206                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1207                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1208                                                        void (*callback)(GtkAdjustment *, GObject *),
1209                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1211     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
1212                                                              lower, upper, step, page, page ) );
1213     if (us) {
1214         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1215     }
1217     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1219     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1221     if ( (descrCount > 0) && descrLabels && descrValues ) {
1222         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1223     }
1225     if ( focusTarget ) {
1226         ege_adjustment_action_set_focuswidget( act, focusTarget );
1227     }
1229     if ( altx && altx_mark ) {
1230         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1231     }
1233     if ( dataKludge ) {
1234         g_object_set_data( dataKludge, data, adj );
1235     }
1237     // Using a cast just to make sure we pass in the right kind of function pointer
1238     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1240     return act;
1244 #define MODE_LABEL_WIDTH 70
1246 //########################
1247 //##       Star         ##
1248 //########################
1250 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1252     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1254     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1255         // do not remember prefs if this call is initiated by an undo change, because undoing object
1256         // creation sets bogus values to its attributes before it is deleted
1257         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1258     }
1260     // quit if run by the attr_changed listener
1261     if (g_object_get_data( dataKludge, "freeze" )) {
1262         return;
1263     }
1265     // in turn, prevent listener from responding
1266     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1268     bool modmade = false;
1270     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1271     GSList const *items = selection->itemList();
1272     for (; items != NULL; items = items->next) {
1273         if (SP_IS_STAR((SPItem *) items->data)) {
1274             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1275             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1276             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1277                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1278                                     + M_PI / (gint)adj->value));
1279             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1280             modmade = true;
1281         }
1282     }
1283     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1284                                    _("Star: Change number of corners"));
1286     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1289 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1291     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1293     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1294         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1295     }
1297     // quit if run by the attr_changed listener
1298     if (g_object_get_data( dataKludge, "freeze" )) {
1299         return;
1300     }
1302     // in turn, prevent listener from responding
1303     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1305     bool modmade = false;
1306     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1307     GSList const *items = selection->itemList();
1308     for (; items != NULL; items = items->next) {
1309         if (SP_IS_STAR((SPItem *) items->data)) {
1310             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1312             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1313             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1314             if (r2 < r1) {
1315                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1316             } else {
1317                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1318             }
1320             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1321             modmade = true;
1322         }
1323     }
1325     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1326                                    _("Star: Change spoke ratio"));
1328     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1331 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1333     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1334     bool flat = ege_select_one_action_get_active( act ) == 0;
1336     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1337         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1338                                     flat ? "true" : "false" );
1339     }
1341     // quit if run by the attr_changed listener
1342     if (g_object_get_data( dataKludge, "freeze" )) {
1343         return;
1344     }
1346     // in turn, prevent listener from responding
1347     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1349     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1350     GSList const *items = selection->itemList();
1351     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1352     bool modmade = false;
1354     if ( prop_action ) {
1355         gtk_action_set_sensitive( prop_action, !flat );
1356     }
1358     for (; items != NULL; items = items->next) {
1359         if (SP_IS_STAR((SPItem *) items->data)) {
1360             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1361             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1362             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1363             modmade = true;
1364         }
1365     }
1367     if (modmade) {
1368         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1369                          flat ? _("Make polygon") : _("Make star"));
1370     }
1372     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1375 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1377     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1379     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1380         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1381     }
1383     // quit if run by the attr_changed listener
1384     if (g_object_get_data( dataKludge, "freeze" )) {
1385         return;
1386     }
1388     // in turn, prevent listener from responding
1389     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1391     bool modmade = false;
1393     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1394     GSList const *items = selection->itemList();
1395     for (; items != NULL; items = items->next) {
1396         if (SP_IS_STAR((SPItem *) items->data)) {
1397             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1398             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1399             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1400             modmade = true;
1401         }
1402     }
1403     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1404                                    _("Star: Change rounding"));
1406     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1409 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1411     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1413     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1414         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1415     }
1417     // quit if run by the attr_changed listener
1418     if (g_object_get_data( dataKludge, "freeze" )) {
1419         return;
1420     }
1422     // in turn, prevent listener from responding
1423     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1425     bool modmade = false;
1427     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1428     GSList const *items = selection->itemList();
1429     for (; items != NULL; items = items->next) {
1430         if (SP_IS_STAR((SPItem *) items->data)) {
1431             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1432             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1433             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1434             modmade = true;
1435         }
1436     }
1437     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1438                                    _("Star: Change randomization"));
1440     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1444 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1445                                        gchar const *old_value, gchar const *new_value,
1446                                        bool is_interactive, gpointer data)
1448     GtkWidget *tbl = GTK_WIDGET(data);
1450     // quit if run by the _changed callbacks
1451     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1452         return;
1453     }
1455     // in turn, prevent callbacks from responding
1456     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1458     GtkAdjustment *adj = 0;
1460     if (!strcmp(name, "inkscape:randomized")) {
1461         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1462         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1463     } else if (!strcmp(name, "inkscape:rounded")) {
1464         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1465         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1466     } else if (!strcmp(name, "inkscape:flatsided")) {
1467         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1468         char const *flatsides = repr->attribute("inkscape:flatsided");
1469         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1470         if ( flatsides && !strcmp(flatsides,"false") ) {
1471             ege_select_one_action_set_active( flat_action, 1 );
1472             gtk_action_set_sensitive( prop_action, TRUE );
1473         } else {
1474             ege_select_one_action_set_active( flat_action, 0 );
1475             gtk_action_set_sensitive( prop_action, FALSE );
1476         }
1477     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1478         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1479         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1480         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1481         if (r2 < r1) {
1482             gtk_adjustment_set_value(adj, r2/r1);
1483         } else {
1484             gtk_adjustment_set_value(adj, r1/r2);
1485         }
1486     } else if (!strcmp(name, "sodipodi:sides")) {
1487         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1488         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1489     }
1491     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1495 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1497     NULL, /* child_added */
1498     NULL, /* child_removed */
1499     star_tb_event_attr_changed,
1500     NULL, /* content_changed */
1501     NULL  /* order_changed */
1502 };
1505 /**
1506  *  \param selection Should not be NULL.
1507  */
1508 static void
1509 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1511     int n_selected = 0;
1512     Inkscape::XML::Node *repr = NULL;
1514     purge_repr_listener( tbl, tbl );
1516     for (GSList const *items = selection->itemList();
1517          items != NULL;
1518          items = items->next)
1519     {
1520         if (SP_IS_STAR((SPItem *) items->data)) {
1521             n_selected++;
1522             repr = SP_OBJECT_REPR((SPItem *) items->data);
1523         }
1524     }
1526     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1528     if (n_selected == 0) {
1529         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1530     } else if (n_selected == 1) {
1531         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1533         if (repr) {
1534             g_object_set_data( tbl, "repr", repr );
1535             Inkscape::GC::anchor(repr);
1536             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1537             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1538         }
1539     } else {
1540         // FIXME: implement averaging of all parameters for multiple selected stars
1541         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1542         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1543     }
1547 static void sp_stb_defaults( GtkWidget *widget, GObject *dataKludge )
1549     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1550     // callbacks to lump all the changes for all selected objects in one undo step
1552     GtkAdjustment *adj = 0;
1554     // fixme: make settable in prefs!
1555     gint mag = 5;
1556     gdouble prop = 0.5;
1557     gboolean flat = FALSE;
1558     gdouble randomized = 0;
1559     gdouble rounded = 0;
1561     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1562     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1564     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1565     gtk_action_set_sensitive( sb2, !flat );
1567     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1568     gtk_adjustment_set_value(adj, mag);
1569     gtk_adjustment_value_changed(adj);
1571     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1572     gtk_adjustment_set_value(adj, prop);
1573     gtk_adjustment_value_changed(adj);
1575     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1576     gtk_adjustment_set_value(adj, rounded);
1577     gtk_adjustment_value_changed(adj);
1579     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1580     gtk_adjustment_set_value(adj, randomized);
1581     gtk_adjustment_value_changed(adj);
1585 void
1586 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1588     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1589     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1590     GtkWidget *l = gtk_label_new(NULL);
1591     gtk_label_set_markup(GTK_LABEL(l), title);
1592     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1593     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1594     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1598 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1600     {
1601         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1602         ege_output_action_set_use_markup( act, TRUE );
1603         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1604         g_object_set_data( holder, "mode_action", act );
1605     }
1607     {
1608         //EgeAdjustmentAction* calligraphy_angle = 0;
1609         EgeAdjustmentAction* eact = 0;
1610         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1611         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1613         /* Flatsided checkbox */
1614         {
1615             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1617             GtkTreeIter iter;
1618             gtk_list_store_append( model, &iter );
1619             gtk_list_store_set( model, &iter,
1620                                 0, _("Polygon"),
1621                                 1, _("Regular polygon (with one handle) instead of a star"),
1622                                 2, "star_flat",
1623                                 -1 );
1625             gtk_list_store_append( model, &iter );
1626             gtk_list_store_set( model, &iter,
1627                                 0, _("Star"),
1628                                 1, _("Star instead of a regular polygon (with one handle)"),
1629                                 2, "star_angled",
1630                                 -1 );
1632             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
1633             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1634             g_object_set_data( holder, "flat_action", act );
1636             ege_select_one_action_set_appearance( act, "full" );
1637             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1638             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1639             ege_select_one_action_set_icon_column( act, 2 );
1640             ege_select_one_action_set_tooltip_column( act, 1  );
1642             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1643             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1644         }
1646         /* Magnitude */
1647         //gchar const* labels[] = {_("tri (default)"), _("quad"), _("pent")};
1648         //gdouble values[] = {3, 4, 5};
1649         eact = create_adjustment_action( "MagnitudeAction",
1650                                          _("Corners:"), _("Number of corners of a polygon or star"),
1651                                          "tools.shapes.star", "magnitude", 3,
1652                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1653                                          3, 1024, 1, 5,
1654                                          0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
1655                                          sp_stb_magnitude_value_changed,
1656                                          1.0, 0 );
1657         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1658         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1660         /* Spoke ratio */
1661         //gchar const* labels2[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1662         //gdouble values2[] = {-90, 0, 30, 90};
1663         eact = create_adjustment_action( "SpokeAction",
1664                                          _("Spoke ratio:"),
1665                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1666                                          // Base radius is the same for the closest handle.
1667                                          _("Base radius to tip radius ratio"),
1668                                          "tools.shapes.star", "proportion", 0.5,
1669                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1670                                          0.01, 1.0, 0.01, 0.1,
1671                                          0, 0, 0, // labels2, values2, G_N_ELEMENTS(labels2),
1672                                          sp_stb_proportion_value_changed );
1673         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1674         g_object_set_data( holder, "prop_action", eact );
1676         if ( !isFlatSided ) {
1677             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1678         } else {
1679             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1680         }
1682         /* Roundedness */
1683         //gchar const* labels3[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1684         //gdouble values3[] = {-90, 0, 30, 90};
1685         eact = create_adjustment_action( "RoundednessAction",
1686                                          _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1687                                          "tools.shapes.star", "rounded", 0.0,
1688                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1689                                          -100.0, 100.0, 0.01, 0.1,
1690                                          0, 0, 0, // labels3, values3, G_N_ELEMENTS(labels3),
1691                                          sp_stb_rounded_value_changed );
1692         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1693         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1695         /* Randomization */
1696         //gchar const* labels4[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1697         //gdouble values4[] = {-90, 0, 30, 90};
1698         eact = create_adjustment_action( "RandomizationAction",
1699                                          _("Randomized:"), _("Scatter randomly the corners and angles"),
1700                                          "tools.shapes.star", "randomized", 0.0,
1701                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1702                                          -10.0, 10.0, 0.001, 0.01,
1703                                          0, 0, 0, // labels4, values4, G_N_ELEMENTS(labels4),
1704                                          sp_stb_randomized_value_changed, 0.1, 3 );
1705         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1706         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1707     }
1709     {
1710         /* Reset */
1711         {
1712             GtkAction* act = gtk_action_new( "StarResetAction",
1713                                              _("Defaults"),
1714                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1715                                              GTK_STOCK_CLEAR );
1716             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1717             gtk_action_group_add_action( mainActions, act );
1718             gtk_action_set_sensitive( act, TRUE );
1719         }
1720     }
1722     sigc::connection *connection = new sigc::connection(
1723         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1724         );
1725     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1726     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1730 //########################
1731 //##       Rect         ##
1732 //########################
1734 static void sp_rtb_sensitivize( GObject *tbl )
1736     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1737     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1738     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1740     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1741         gtk_action_set_sensitive( not_rounded, FALSE );
1742     } else {
1743         gtk_action_set_sensitive( not_rounded, TRUE );
1744     }
1748 static void
1749 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1750                           void (*setter)(SPRect *, gdouble))
1752     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1754     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1755     SPUnit const *unit = tracker->getActiveUnit();
1757     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1758         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1759     }
1761     // quit if run by the attr_changed listener
1762     if (g_object_get_data( tbl, "freeze" )) {
1763         return;
1764     }
1766     // in turn, prevent listener from responding
1767     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1769     bool modmade = false;
1770     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1771     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1772         if (SP_IS_RECT(items->data)) {
1773             if (adj->value != 0) {
1774                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
1775             } else {
1776                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
1777             }
1778             modmade = true;
1779         }
1780     }
1782     sp_rtb_sensitivize( tbl );
1784     if (modmade) {
1785         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
1786                                    _("Change rectangle"));
1787     }
1789     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1792 static void
1793 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
1795     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
1798 static void
1799 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
1801     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
1804 static void
1805 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
1807     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
1810 static void
1811 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
1813     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
1818 static void
1819 sp_rtb_defaults( GtkWidget *widget, GObject *obj)
1821     GtkAdjustment *adj = 0;
1823     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
1824     gtk_adjustment_set_value(adj, 0.0);
1825     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
1826     gtk_adjustment_value_changed(adj);
1828     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
1829     gtk_adjustment_set_value(adj, 0.0);
1830     gtk_adjustment_value_changed(adj);
1832     sp_rtb_sensitivize( obj );
1835 static void rect_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1836                                        gchar const *old_value, gchar const *new_value,
1837                                        bool is_interactive, gpointer data)
1839     GObject *tbl = G_OBJECT(data);
1841     // quit if run by the _changed callbacks
1842     if (g_object_get_data( tbl, "freeze" )) {
1843         return;
1844     }
1846     // in turn, prevent callbacks from responding
1847     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
1849     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1850     SPUnit const *unit = tracker->getActiveUnit();
1852     gpointer item = g_object_get_data( tbl, "item" );
1853     if (item && SP_IS_RECT(item)) {
1854         {
1855             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
1856             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
1857             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
1858         }
1860         {
1861             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
1862             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
1863             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
1864         }
1866         {
1867             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
1868             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
1869             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
1870         }
1872         {
1873             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
1874             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
1875             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
1876         }
1877     }
1879     sp_rtb_sensitivize( tbl );
1881     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1885 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
1886     NULL, /* child_added */
1887     NULL, /* child_removed */
1888     rect_tb_event_attr_changed,
1889     NULL, /* content_changed */
1890     NULL  /* order_changed */
1891 };
1893 /**
1894  *  \param selection should not be NULL.
1895  */
1896 static void
1897 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1899     int n_selected = 0;
1900     Inkscape::XML::Node *repr = NULL;
1901     SPItem *item = NULL;
1903     if ( g_object_get_data( tbl, "repr" ) ) {
1904         g_object_set_data( tbl, "item", NULL );
1905     }
1906     purge_repr_listener( tbl, tbl );
1908     for (GSList const *items = selection->itemList();
1909          items != NULL;
1910          items = items->next) {
1911         if (SP_IS_RECT((SPItem *) items->data)) {
1912             n_selected++;
1913             item = (SPItem *) items->data;
1914             repr = SP_OBJECT_REPR(item);
1915         }
1916     }
1918     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1920     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
1922     if (n_selected == 0) {
1923         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1925         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
1926         gtk_action_set_sensitive(w, FALSE);
1927         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
1928         gtk_action_set_sensitive(h, FALSE);
1930     } else if (n_selected == 1) {
1931         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1932         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
1934         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
1935         gtk_action_set_sensitive(w, TRUE);
1936         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
1937         gtk_action_set_sensitive(h, TRUE);
1939         if (repr) {
1940             g_object_set_data( tbl, "repr", repr );
1941             g_object_set_data( tbl, "item", item );
1942             Inkscape::GC::anchor(repr);
1943             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
1944             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
1945         }
1946     } else {
1947         // FIXME: implement averaging of all parameters for multiple selected
1948         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1949         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1950         sp_rtb_sensitivize( tbl );
1951     }
1955 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1957     EgeAdjustmentAction* eact = 0;
1959     {
1960         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
1961         ege_output_action_set_use_markup( act, TRUE );
1962         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1963         g_object_set_data( holder, "mode_action", act );
1964     }
1966     // rx/ry units menu: create
1967     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1968     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
1969     // fixme: add % meaning per cent of the width/height
1970     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1971     g_object_set_data( holder, "tracker", tracker );
1973     /* W */
1974     {
1975         eact = create_adjustment_action( "RectWidthAction",
1976                                          _("W:"), _("Width of rectangle"),
1977                                          "tools.shapes.rect", "width", 0,
1978                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
1979                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1980                                          0, 0, 0,
1981                                          sp_rtb_width_value_changed );
1982         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1983         g_object_set_data( holder, "width_action", eact );
1984         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1985         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1986     }
1988     /* H */
1989     {
1990         eact = create_adjustment_action( "RectHeightAction",
1991                                          _("H:"), _("Height of rectangle"),
1992                                          "tools.shapes.rect", "height", 0,
1993                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1994                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1995                                          0, 0, 0,
1996                                          sp_rtb_height_value_changed );
1997         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1998         g_object_set_data( holder, "height_action", eact );
1999         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2000         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2001     }
2003     /* rx */
2004     {
2005         eact = create_adjustment_action( "RadiusXAction",
2006                                          _("Rx:"), _("Horizontal radius of rounded corners"),
2007                                          "tools.shapes.rect", "rx", 0,
2008                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2009                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2010                                          0, 0, 0,
2011                                          sp_rtb_rx_value_changed);
2012         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2013         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2014     }
2016     /* ry */
2017     {
2018         eact = create_adjustment_action( "RadiusYAction",
2019                                          _("Ry:"), _("Vertical radius of rounded corners"),
2020                                          "tools.shapes.rect", "ry", 0,
2021                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2022                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2023                                          0, 0, 0,
2024                                          sp_rtb_ry_value_changed);
2025         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2026         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2027     }
2029     // add the units menu
2030     {
2031         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), _("") );
2032         gtk_action_group_add_action( mainActions, act );
2033     }
2035     /* Reset */
2036     {
2037         InkAction* inky = ink_action_new( "RectResetAction",
2038                                           _("Not rounded"),
2039                                           _("Make corners sharp"),
2040                                           "squared_corner",
2041                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2042         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2043         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2044         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2045         g_object_set_data( holder, "not_rounded", inky );
2046     }
2048     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2049     sp_rtb_sensitivize( holder );
2051     sigc::connection *connection = new sigc::connection(
2052         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2053         );
2054     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2055     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2058 //########################
2059 //##       Spiral       ##
2060 //########################
2062 static void
2063 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2065     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2067     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2068         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2069     }
2071     // quit if run by the attr_changed listener
2072     if (g_object_get_data( tbl, "freeze" )) {
2073         return;
2074     }
2076     // in turn, prevent listener from responding
2077     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2079     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2081     bool modmade = false;
2082     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2083          items != NULL;
2084          items = items->next)
2085     {
2086         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2087             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2088             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2089             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2090             modmade = true;
2091         }
2092     }
2094     g_free(namespaced_name);
2096     if (modmade) {
2097         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2098                                    _("Change spiral"));
2099     }
2101     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2104 static void
2105 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2107     sp_spl_tb_value_changed(adj, tbl, "revolution");
2110 static void
2111 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2113     sp_spl_tb_value_changed(adj, tbl, "expansion");
2116 static void
2117 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2119     sp_spl_tb_value_changed(adj, tbl, "t0");
2122 static void
2123 sp_spl_tb_defaults(GtkWidget *widget, GtkObject *obj)
2125     GtkWidget *tbl = GTK_WIDGET(obj);
2127     GtkAdjustment *adj;
2129     // fixme: make settable
2130     gdouble rev = 5;
2131     gdouble exp = 1.0;
2132     gdouble t0 = 0.0;
2134     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2135     gtk_adjustment_set_value(adj, rev);
2136     gtk_adjustment_value_changed(adj);
2138     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2139     gtk_adjustment_set_value(adj, exp);
2140     gtk_adjustment_value_changed(adj);
2142     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2143     gtk_adjustment_set_value(adj, t0);
2144     gtk_adjustment_value_changed(adj);
2146     spinbutton_defocus(GTK_OBJECT(tbl));
2150 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2151                                          gchar const *old_value, gchar const *new_value,
2152                                          bool is_interactive, gpointer data)
2154     GtkWidget *tbl = GTK_WIDGET(data);
2156     // quit if run by the _changed callbacks
2157     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2158         return;
2159     }
2161     // in turn, prevent callbacks from responding
2162     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2164     GtkAdjustment *adj;
2165     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2166     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2168     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2169     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2171     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2172     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2174     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2178 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2179     NULL, /* child_added */
2180     NULL, /* child_removed */
2181     spiral_tb_event_attr_changed,
2182     NULL, /* content_changed */
2183     NULL  /* order_changed */
2184 };
2186 static void
2187 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2189     int n_selected = 0;
2190     Inkscape::XML::Node *repr = NULL;
2192     purge_repr_listener( tbl, tbl );
2194     for (GSList const *items = selection->itemList();
2195          items != NULL;
2196          items = items->next)
2197     {
2198         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2199             n_selected++;
2200             repr = SP_OBJECT_REPR((SPItem *) items->data);
2201         }
2202     }
2204     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2206     if (n_selected == 0) {
2207         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2208     } else if (n_selected == 1) {
2209         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2211         if (repr) {
2212             g_object_set_data( tbl, "repr", repr );
2213             Inkscape::GC::anchor(repr);
2214             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2215             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2216         }
2217     } else {
2218         // FIXME: implement averaging of all parameters for multiple selected
2219         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2220         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2221     }
2225 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2227     EgeAdjustmentAction* eact = 0;
2229     {
2230         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2231         ege_output_action_set_use_markup( act, TRUE );
2232         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2233         g_object_set_data( holder, "mode_action", act );
2234     }
2236     /* Revolution */
2237     {
2238         eact = create_adjustment_action( "SpiralRevolutionAction",
2239                                          _("Turns:"), _("Number of revolutions"),
2240                                          "tools.shapes.spiral", "revolution", 3.0,
2241                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2242                                          0.01, 1024.0, 0.1, 1.0,
2243                                          0, 0, 0,
2244                                          sp_spl_tb_revolution_value_changed, 1, 2);
2245         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2246     }
2248     /* Expansion */
2249     {
2250         eact = create_adjustment_action( "SpiralExpansionAction",
2251                                          _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2252                                          "tools.shapes.spiral", "expansion", 1.0,
2253                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2254                                          0.0, 1000.0, 0.01, 1.0,
2255                                          0, 0, 0,
2256                                          sp_spl_tb_expansion_value_changed);
2257         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2258     }
2260     /* T0 */
2261     {
2262         eact = create_adjustment_action( "SpiralT0Action",
2263                                          _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2264                                          "tools.shapes.spiral", "t0", 0.0,
2265                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2266                                          0.0, 0.999, 0.01, 1.0,
2267                                          0, 0, 0,
2268                                          sp_spl_tb_t0_value_changed);
2269         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2270     }
2272     /* Reset */
2273     {
2274         InkAction* inky = ink_action_new( "SpiralResetAction",
2275                                           _("Defaults"),
2276                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2277                                           GTK_STOCK_CLEAR,
2278                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2279         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2280         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2281     }
2284     sigc::connection *connection = new sigc::connection(
2285         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2286         );
2287     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2288     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2291 //########################
2292 //##     Pen/Pencil    ##
2293 //########################
2296 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2298     // Put stuff here
2301 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2303     // Put stuff here
2307 //########################
2308 //##     Calligraphy    ##
2309 //########################
2311 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
2313     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2316 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
2318     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2321 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
2323     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2326 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2328     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2331 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
2333     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
2336 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
2338     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
2341 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
2343     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
2346 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
2348     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
2351 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer data )
2353     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2356 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer data )
2358     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
2361 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
2363     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
2365     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
2368 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
2370     // FIXME: make defaults settable via Inkscape Options
2371     struct KeyValue {
2372         char const *key;
2373         double value;
2374     } const key_values[] = {
2375         {"mass", 0.02},
2376         {"wiggle", 0.0},
2377         {"angle", 30.0},
2378         {"width", 15},
2379         {"thinning", 0.1},
2380         {"tremor", 0.0},
2381         {"flatness", 0.9},
2382         {"cap_rounding", 0.0}
2383     };
2385     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
2386         KeyValue const &kv = key_values[i];
2387         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
2388         if ( adj ) {
2389             gtk_adjustment_set_value(adj, kv.value);
2390         }
2391     }
2395 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2397     {
2398         EgeAdjustmentAction* calligraphy_angle = 0;
2400         {
2401         /* Width */
2402         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
2403         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2404         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
2405                                          _("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
2406                                          "tools.calligraphic", "width", 15,
2407                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
2408                                          1, 100, 1.0, 10.0,
2409                                          labels, values, G_N_ELEMENTS(labels),
2410                                          sp_ddc_width_value_changed,  0.01, 0, 100 );
2411         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2412         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2413         }
2415         {
2416         /* Thinning */
2417             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
2418             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
2419         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
2420                                          _("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
2421                                          "tools.calligraphic", "thinning", 0.1,
2422                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2423                                          -1.0, 1.0, 0.01, 0.1,
2424                                          labels, values, G_N_ELEMENTS(labels),
2425                                          sp_ddc_velthin_value_changed, 0.01, 2);
2426         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2427         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2428         }
2430         {
2431         /* Angle */
2432         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
2433         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2434         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
2435                                          _("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
2436                                          "tools.calligraphic", "angle", 30,
2437                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
2438                                          -90.0, 90.0, 1.0, 10.0,
2439                                          labels, values, G_N_ELEMENTS(labels),
2440                                          sp_ddc_angle_value_changed, 1, 0 );
2441         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2442         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2443         calligraphy_angle = eact;
2444         }
2446         {
2447         /* Fixation */
2448             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
2449         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
2450         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
2451                                          _("Fixation:"), _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
2452                                          "tools.calligraphic", "flatness", 0.9,
2453                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2454                                          0.0, 1.0, 0.01, 0.1,
2455                                          labels, values, G_N_ELEMENTS(labels),
2456                                          sp_ddc_flatness_value_changed, 0.01, 2 );
2457         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2458         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2459         }
2461         {
2462         /* Cap Rounding */
2463             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
2464         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
2465         // TRANSLATORS: "cap" means "end" (both start and finish) here
2466         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
2467                                          _("Caps:"), _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
2468                                          "tools.calligraphic", "cap_rounding", 0.0,
2469                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2470                                          0.0, 5.0, 0.01, 0.1,
2471                                          labels, values, G_N_ELEMENTS(labels),
2472                                          sp_ddc_cap_rounding_value_changed, 0.01, 2 );
2473         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2474         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2475         }
2477         {
2478         /* Tremor */
2479             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
2480         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
2481         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
2482                                          _("Tremor:"), _("Increase to make strokes rugged and trembling"),
2483                                          "tools.calligraphic", "tremor", 0.0,
2484                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2485                                          0.0, 1.0, 0.01, 0.1,
2486                                          labels, values, G_N_ELEMENTS(labels),
2487                                          sp_ddc_tremor_value_changed, 0.01, 2 );
2489         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2490         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2491         }
2493         {
2494         /* Wiggle */
2495         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
2496         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
2497         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
2498                                          _("Wiggle:"), _("Increase to make the pen waver and wiggle"),
2499                                          "tools.calligraphic", "wiggle", 0.0,
2500                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2501                                          0.0, 1.0, 0.01, 0.1,
2502                                          labels, values, G_N_ELEMENTS(labels),
2503                                          sp_ddc_wiggle_value_changed, 0.01, 2 );
2504         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2505         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2506         }
2508         {
2509         /* Mass */
2510             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
2511         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
2512         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
2513                                          _("Mass:"), _("Increase to make the pen drag behind, as if slowed by inertia"),
2514                                          "tools.calligraphic", "mass", 0.02,
2515                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2516                                          0.0, 1.0, 0.01, 0.1,
2517                                          labels, values, G_N_ELEMENTS(labels),
2518                                          sp_ddc_mass_value_changed, 0.01, 2 );
2519         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2520         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2521         }
2524         /* Trace Background button */
2525         {
2526             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
2527                                                           _("Trace Background"),
2528                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
2529                                                           "trace_background",
2530                                                           Inkscape::ICON_SIZE_DECORATION );
2531             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2532             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
2533             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
2534         }
2536         /* Use Pressure button */
2537         {
2538             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
2539                                                           _("Pressure"),
2540                                                           _("Use the pressure of the input device to alter the width of the pen"),
2541                                                           "use_pressure",
2542                                                           Inkscape::ICON_SIZE_DECORATION );
2543             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2544             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
2545             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
2546         }
2548         /* Use Tilt button */
2549         {
2550             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
2551                                                           _("Tilt"),
2552                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
2553                                                           "use_tilt",
2554                                                           Inkscape::ICON_SIZE_DECORATION );
2555             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2556             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
2557             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2558             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2559         }
2561         /* Reset */
2562         {
2563             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
2564                                              _("Defaults"),
2565                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2566                                              GTK_STOCK_CLEAR );
2567             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
2568             gtk_action_group_add_action( mainActions, act );
2569             gtk_action_set_sensitive( act, TRUE );
2570         }
2571     }
2575 //########################
2576 //##    Circle / Arc    ##
2577 //########################
2579 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
2581     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
2582     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
2584     if (v1 == 0 && v2 == 0) {
2585         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
2586             gtk_action_set_sensitive( ocb, FALSE );
2587             gtk_action_set_sensitive( make_whole, FALSE );
2588         }
2589     } else {
2590         gtk_action_set_sensitive( ocb, TRUE );
2591         gtk_action_set_sensitive( make_whole, TRUE );
2592     }
2595 static void
2596 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
2598     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2600     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2601         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
2602     }
2604     // quit if run by the attr_changed listener
2605     if (g_object_get_data( tbl, "freeze" )) {
2606         return;
2607     }
2609     // in turn, prevent listener from responding
2610     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2612     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2614     bool modmade = false;
2615     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2616          items != NULL;
2617          items = items->next)
2618     {
2619         SPItem *item = SP_ITEM(items->data);
2621         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
2623             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
2624             SPArc *arc = SP_ARC(item);
2626             if (!strcmp(value_name, "start"))
2627                 ge->start = (adj->value * M_PI)/ 180;
2628             else
2629                 ge->end = (adj->value * M_PI)/ 180;
2631             sp_genericellipse_normalize(ge);
2632             ((SPObject *)arc)->updateRepr();
2633             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
2635             modmade = true;
2636         }
2637     }
2639     g_free(namespaced_name);
2641     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
2643     sp_arctb_sensitivize( tbl, adj->value, other->value );
2645     if (modmade) {
2646         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
2647                                    _("Arc: Change start/end"));
2648     }
2650     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2654 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
2656     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
2659 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
2661     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
2664 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
2666     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2667     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2668         if ( ege_select_one_action_get_active( act ) != 0 ) {
2669             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
2670         } else {
2671             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
2672         }
2673     }
2675     // quit if run by the attr_changed listener
2676     if (g_object_get_data( tbl, "freeze" )) {
2677         return;
2678     }
2680     // in turn, prevent listener from responding
2681     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2683     bool modmade = false;
2685     if ( ege_select_one_action_get_active(act) != 0 ) {
2686         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2687              items != NULL;
2688              items = items->next)
2689         {
2690             if (SP_IS_ARC((SPItem *) items->data)) {
2691                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2692                 repr->setAttribute("sodipodi:open", "true");
2693                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2694                 modmade = true;
2695             }
2696         }
2697     } else {
2698         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2699              items != NULL;
2700              items = items->next)
2701         {
2702             if (SP_IS_ARC((SPItem *) items->data))    {
2703                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2704                 repr->setAttribute("sodipodi:open", NULL);
2705                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2706                 modmade = true;
2707             }
2708         }
2709     }
2711     if (modmade) {
2712         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
2713                                    _("Arc: Change open/closed"));
2714     }
2716     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2719 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
2721     GtkAdjustment *adj;
2722     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
2723     gtk_adjustment_set_value(adj, 0.0);
2724     gtk_adjustment_value_changed(adj);
2726     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
2727     gtk_adjustment_set_value(adj, 0.0);
2728     gtk_adjustment_value_changed(adj);
2730     spinbutton_defocus( GTK_OBJECT(obj) );
2733 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2734                                       gchar const *old_value, gchar const *new_value,
2735                                       bool is_interactive, gpointer data)
2737     GObject *tbl = G_OBJECT(data);
2739     // quit if run by the _changed callbacks
2740     if (g_object_get_data( tbl, "freeze" )) {
2741         return;
2742     }
2744     // in turn, prevent callbacks from responding
2745     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2747     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
2748     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
2750     GtkAdjustment *adj1,*adj2;
2751     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
2752     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
2753     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
2754     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
2756     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
2758     char const *openstr = NULL;
2759     openstr = repr->attribute("sodipodi:open");
2760     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
2762     if (openstr) {
2763         ege_select_one_action_set_active( ocb, 1 );
2764     } else {
2765         ege_select_one_action_set_active( ocb, 0 );
2766     }
2768     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2771 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
2772     NULL, /* child_added */
2773     NULL, /* child_removed */
2774     arc_tb_event_attr_changed,
2775     NULL, /* content_changed */
2776     NULL  /* order_changed */
2777 };
2780 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2782     int n_selected = 0;
2783     Inkscape::XML::Node *repr = NULL;
2785     purge_repr_listener( tbl, tbl );
2787     for (GSList const *items = selection->itemList();
2788          items != NULL;
2789          items = items->next)
2790     {
2791         if (SP_IS_ARC((SPItem *) items->data)) {
2792             n_selected++;
2793             repr = SP_OBJECT_REPR((SPItem *) items->data);
2794         }
2795     }
2797     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2799     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2800     if (n_selected == 0) {
2801         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2802     } else if (n_selected == 1) {
2803         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2804         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2806         if (repr) {
2807             g_object_set_data( tbl, "repr", repr );
2808             Inkscape::GC::anchor(repr);
2809             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
2810             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
2811         }
2812     } else {
2813         // FIXME: implement averaging of all parameters for multiple selected
2814         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2815         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2816         sp_arctb_sensitivize( tbl, 1, 0 );
2817     }
2821 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2823     EgeAdjustmentAction* eact = 0;
2826     {
2827         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
2828         ege_output_action_set_use_markup( act, TRUE );
2829         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2830         g_object_set_data( holder, "mode_action", act );
2831     }
2833     /* Start */
2834     {
2835         eact = create_adjustment_action( "ArcStartAction",
2836                                          _("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
2837                                          "tools.shapes.arc", "start", 0.0,
2838                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
2839                                          -360.0, 360.0, 1.0, 10.0,
2840                                          0, 0, 0,
2841                                          sp_arctb_start_value_changed);
2842         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2843     }
2845     /* End */
2846     {
2847         eact = create_adjustment_action( "ArcEndAction",
2848                                          _("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
2849                                          "tools.shapes.arc", "end", 0.0,
2850                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2851                                          -360.0, 360.0, 1.0, 10.0,
2852                                          0, 0, 0,
2853                                          sp_arctb_end_value_changed);
2854         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2855     }
2857     /* Segments / Pie checkbox */
2858     {
2859         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2861         GtkTreeIter iter;
2862         gtk_list_store_append( model, &iter );
2863         gtk_list_store_set( model, &iter,
2864                             0, _("Closed arc"),
2865                             1, _("Switch to segment (closed shape with two radii)"),
2866                             2, "circle_closed_arc",
2867                             -1 );
2869         gtk_list_store_append( model, &iter );
2870         gtk_list_store_set( model, &iter,
2871                             0, _("Open Arc"),
2872                             1, _("Switch to arc (unclosed shape)"),
2873                             2, "circle_open_arc",
2874                             -1 );
2876         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
2877         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2878         g_object_set_data( holder, "open_action", act );
2880         ege_select_one_action_set_appearance( act, "full" );
2881         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2882         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2883         ege_select_one_action_set_icon_column( act, 2 );
2884         ege_select_one_action_set_tooltip_column( act, 1  );
2886         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
2887         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
2888         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
2889         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
2890     }
2892     /* Make Whole */
2893     {
2894         InkAction* inky = ink_action_new( "ArcResetAction",
2895                                           _("Make whole"),
2896                                           _("Make the shape a whole ellipse, not arc or segment"),
2897                                           "reset_circle",
2898                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2899         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
2900         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2901         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2902         g_object_set_data( holder, "make_whole", inky );
2903     }
2905     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
2906     // sensitivize make whole and open checkbox
2907     {
2908         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
2909         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
2910         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
2911     }
2914     sigc::connection *connection = new sigc::connection(
2915         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
2916         );
2917     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2918     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2924 // toggle button callbacks and updaters
2926 //########################
2927 //##      Dropper       ##
2928 //########################
2930 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
2931     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
2932     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
2933     if ( set_action ) {
2934         if ( gtk_toggle_action_get_active( act ) ) {
2935             gtk_action_set_sensitive( set_action, TRUE );
2936         } else {
2937             gtk_action_set_sensitive( set_action, FALSE );
2938         }
2939     }
2941     spinbutton_defocus(GTK_OBJECT(tbl));
2944 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
2945     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
2946     spinbutton_defocus(GTK_OBJECT(tbl));
2950 /**
2951  * Dropper auxiliary toolbar construction and setup.
2952  *
2953  * TODO: Would like to add swatch of current color.
2954  * TODO: Add queue of last 5 or so colors selected with new swatches so that
2955  *       can drag and drop places. Will provide a nice mixing palette.
2956  */
2957 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2959     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
2961     {
2962         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
2963                                                       _("Pick alpha"),
2964                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
2965                                                       "color_alpha_get",
2966                                                       Inkscape::ICON_SIZE_DECORATION );
2967         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2968         g_object_set_data( holder, "pick_action", act );
2969         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
2970         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
2971     }
2973     {
2974         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
2975                                                       _("Set alpha"),
2976                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
2977                                                       "color_alpha_set",
2978                                                       Inkscape::ICON_SIZE_DECORATION );
2979         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2980         g_object_set_data( holder, "set_action", act );
2981         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
2982         // make sure it's disabled if we're not picking alpha
2983         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
2984         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
2985     }
2989 //########################
2990 //##    Text Toolbox    ##
2991 //########################
2992 /*
2993 static void
2994 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
2996     //Call back for letter sizing spinbutton
2999 static void
3000 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3002     //Call back for line height spinbutton
3005 static void
3006 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3008     //Call back for horizontal kerning spinbutton
3011 static void
3012 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3014     //Call back for vertical kerning spinbutton
3017 static void
3018 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3020     //Call back for letter rotation spinbutton
3021 }*/
3023 namespace {
3025 bool visible = false;
3027 void
3028 sp_text_toolbox_selection_changed (Inkscape::Selection *selection, GObject *tbl)
3031     SPStyle *query =
3032         sp_style_new ();
3034     int result_family =
3035         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3037     int result_style =
3038         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3040     int result_numbers =
3041         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3043     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3045     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3046     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3047     {
3048         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3050         if (repr)
3051         {
3052             sp_style_read_from_repr (query, repr);
3053         }
3054         else
3055         {
3056             return;
3057         }
3058     }
3060     if (query->text)
3061     {
3062         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3063             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3064             gtk_entry_set_text (GTK_ENTRY (entry), "");
3066         } else if (query->text->font_family.value) {
3068             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3069             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3071             Gtk::TreePath path;
3072             try {
3073                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3074             } catch (...) {
3075                 return;
3076             }
3078             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3079             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3081             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3083             gtk_tree_selection_select_path (tselection, path.gobj());
3084             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3086             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3087         }
3089         //Size
3090         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3091         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3092         g_object_set_data (tbl, "size-block", gpointer(1));
3093         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3094         g_object_set_data (tbl, "size-block", gpointer(0));
3095         free (str);
3097         //Anchor
3098         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3099         {
3100             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3101             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3102             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3103             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3104         }
3105         else
3106         {
3107             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3108             {
3109                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3110                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3111                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3112                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3113             }
3114             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3115             {
3116                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3117                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3118                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3119                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3120             }
3121             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3122             {
3123                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3124                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3125                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3126                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3127             }
3128         }
3130         //Style
3131         {
3132             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3134             gboolean active = gtk_toggle_button_get_active (button);
3135             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3137             if (active != check)
3138             {
3139                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3140                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3141                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3142             }
3143         }
3145         {
3146             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3148             gboolean active = gtk_toggle_button_get_active (button);
3149             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3151             if (active != check)
3152             {
3153                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3154                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3155                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3156             }
3157         }
3159         //Orientation
3160         //locking both buttons, changing one affect all group (both)
3161         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3162         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3164         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3165         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3167         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3168         {
3169             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3170         }
3171         else
3172         {
3173             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3174         }
3175         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3176         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3177     }
3180 void
3181 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint flags, GObject *tbl)
3183     sp_text_toolbox_selection_changed (selection, tbl);
3186 void
3187 sp_text_toolbox_subselection_changed (gpointer dragger, GObject *tbl)
3189     sp_text_toolbox_selection_changed (NULL, tbl);
3192 void
3193 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3194                                 GObject             *tbl)
3196     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3197     GtkTreeModel *model = 0;
3198     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3199     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3200     GtkTreeIter   iter;
3201     char         *family = 0;
3203     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3204     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3206     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3207         return;
3208     }
3210     gtk_tree_model_get (model, &iter, 0, &family, -1);
3212     if (g_object_get_data (G_OBJECT (selection), "block"))
3213     {
3214         gtk_entry_set_text (GTK_ENTRY (entry), family);
3215         return;
3216     }
3218     gtk_widget_hide (popdown);
3219     visible = false;
3221     gtk_entry_set_text (GTK_ENTRY (entry), family);
3223     SPStyle *query =
3224         sp_style_new ();
3226     int result_numbers =
3227         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3229     SPCSSAttr *css = sp_repr_css_attr_new ();
3230     sp_repr_css_set_property (css, "font-family", family);
3232     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3233     if (result_numbers == QUERY_STYLE_NOTHING)
3234     {
3235         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3236         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3237     }
3238     else
3239     {
3240         sp_desktop_set_style (desktop, css, true, true);
3241     }
3243     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3244                                    _("Text: Change font family"));
3245     sp_repr_css_attr_unref (css);
3246     free (family);
3247     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3249     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3252 void
3253 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3254                                        GObject      *tbl)
3256     const char *family = gtk_entry_get_text (entry);
3258     try {
3259         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3260         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3261         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3262         gtk_tree_selection_select_path (selection, path.gobj());
3263         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3264         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3265     } catch (...) {
3266         if (family && strlen (family))
3267         {
3268             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3269         }
3270     }
3273 void
3274 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3275                                    gpointer          data)
3277     if (g_object_get_data (G_OBJECT (button), "block")) return;
3278     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3279     int prop = GPOINTER_TO_INT(data);
3281     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3282     SPCSSAttr *css = sp_repr_css_attr_new ();
3284     switch (prop)
3285     {
3286         case 0:
3287         {
3288             sp_repr_css_set_property (css, "text-anchor", "start");
3289             sp_repr_css_set_property (css, "text-align", "start");
3290             break;
3291         }
3292         case 1:
3293         {
3294             sp_repr_css_set_property (css, "text-anchor", "middle");
3295             sp_repr_css_set_property (css, "text-align", "center");
3296             break;
3297         }
3299         case 2:
3300         {
3301             sp_repr_css_set_property (css, "text-anchor", "end");
3302             sp_repr_css_set_property (css, "text-align", "end");
3303             break;
3304         }
3306         case 3:
3307         {
3308             sp_repr_css_set_property (css, "text-anchor", "start");
3309             sp_repr_css_set_property (css, "text-align", "justify");
3310             break;
3311         }
3312     }
3314     SPStyle *query =
3315         sp_style_new ();
3316     int result_numbers =
3317         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3319     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3320     if (result_numbers == QUERY_STYLE_NOTHING)
3321     {
3322         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3323     }
3325     sp_desktop_set_style (desktop, css, true, true);
3326     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3327                                    _("Text: Change alignment"));
3328     sp_repr_css_attr_unref (css);
3330     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3333 void
3334 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
3335                                gpointer          data)
3337     if (g_object_get_data (G_OBJECT (button), "block")) return;
3339     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3340     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3341     int          prop       = GPOINTER_TO_INT(data);
3342     bool         active     = gtk_toggle_button_get_active (button);
3345     switch (prop)
3346     {
3347         case 0:
3348         {
3349             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
3350             break;
3351         }
3353         case 1:
3354         {
3355             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
3356             break;
3357         }
3358     }
3360     SPStyle *query =
3361         sp_style_new ();
3362     int result_numbers =
3363         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3365     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3366     if (result_numbers == QUERY_STYLE_NOTHING)
3367     {
3368         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3369     }
3371     sp_desktop_set_style (desktop, css, true, true);
3372     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3373                                    _("Text: Change font style"));
3374     sp_repr_css_attr_unref (css);
3376     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3379 void
3380 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
3381                                      gpointer         data)
3383     if (g_object_get_data (G_OBJECT (button), "block")) {
3384         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3385         return;
3386     }
3388     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3389     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3390     int          prop       = GPOINTER_TO_INT(data);
3392     switch (prop)
3393     {
3394         case 0:
3395         {
3396             sp_repr_css_set_property (css, "writing-mode", "lr");
3397             break;
3398         }
3400         case 1:
3401         {
3402             sp_repr_css_set_property (css, "writing-mode", "tb");
3403             break;
3404         }
3405     }
3407     SPStyle *query =
3408         sp_style_new ();
3409     int result_numbers =
3410         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3412     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3413     if (result_numbers == QUERY_STYLE_NOTHING)
3414     {
3415         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3416     }
3418     sp_desktop_set_style (desktop, css, true, true);
3419     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3420                                    _("Text: Change orientation"));
3421     sp_repr_css_attr_unref (css);
3423     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3426 gboolean
3427 sp_text_toolbox_size_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
3429     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3430     if (!desktop) return FALSE;
3432     switch (get_group0_keyval (event)) {
3433         case GDK_Escape: // defocus
3434             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3435             return TRUE; // I consumed the event
3436             break;
3437         case GDK_Return: // defocus
3438         case GDK_KP_Enter:
3439             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3440             return TRUE; // I consumed the event
3441             break;
3442     }
3443     return FALSE;
3446 gboolean
3447 sp_text_toolbox_family_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3449     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3450     if (!desktop) return FALSE;
3452     switch (get_group0_keyval (event)) {
3453         case GDK_Escape: // defocus
3454             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3455             sp_text_toolbox_selection_changed (NULL, tbl); // update
3456             return TRUE; // I consumed the event
3457             break;
3458     }
3459     return FALSE;
3462 gboolean
3463 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3465     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3466     if (!desktop) return FALSE;
3468     switch (get_group0_keyval (event)) {
3469         case GDK_Escape: // defocus
3470             gtk_widget_hide (w);
3471             visible = false;
3472             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3473             return TRUE; // I consumed the event
3474             break;
3475     }
3476     return FALSE;
3480 void
3481 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
3482                                GObject     *tbl)
3484     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3486     if (g_object_get_data (tbl, "size-block")) return;
3488 #if GTK_CHECK_VERSION(2,6,0)
3489     char *text = gtk_combo_box_get_active_text (cbox);
3490 #else // GTK_CHECK_VERSION(2,6,0)
3491     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
3492     GtkTreeIter iter;
3493     char *text = NULL;
3495     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
3496         gtk_tree_model_get (model, &iter, 0, &text, -1);
3497 #endif // GTK_CHECK_VERSION(2,6,0)
3499     SPCSSAttr *css = sp_repr_css_attr_new ();
3500     sp_repr_css_set_property (css, "font-size", text);
3501     free (text);
3503     SPStyle *query =
3504         sp_style_new ();
3505     int result_numbers =
3506         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3508     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3509     if (result_numbers == QUERY_STYLE_NOTHING)
3510     {
3511         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3512     }
3514     sp_desktop_set_style (desktop, css, true, true);
3515     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
3516                                    _("Text: Change font size"));
3517     sp_repr_css_attr_unref (css);
3520     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
3521         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3524 void
3525 sp_text_toolbox_text_popdown_clicked    (GtkButton          *button,
3526                                          GObject            *tbl)
3528     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3529     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3530     int x, y;
3532     if (!visible)
3533     {
3534         gdk_window_get_origin (widget->window, &x, &y);
3535         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
3536         gtk_widget_show_all (popdown);
3538         gdk_pointer_grab (widget->window, TRUE,
3539                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
3540                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
3541                                         GDK_POINTER_MOTION_MASK),
3542                           NULL, NULL, GDK_CURRENT_TIME);
3544         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
3546         visible = true;
3547     }
3548     else
3549     {
3550         gdk_pointer_ungrab (GDK_CURRENT_TIME);
3551         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3552         gtk_widget_hide (popdown);
3553         visible = false;
3554     }
3557 gboolean
3558 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
3559                                  GdkEventFocus    *event,
3560                                  GObject          *tbl)
3562     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
3563     return FALSE;
3566 gboolean
3567 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
3568                                    GdkEventFocus    *event,
3569                                    GObject          *tbl)
3571     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3573     gtk_widget_hide (popdown);
3574     visible = false;
3575     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3576     return TRUE;
3579 void
3580 cell_data_func  (GtkTreeViewColumn *column,
3581                  GtkCellRenderer   *cell,
3582                  GtkTreeModel      *tree_model,
3583                  GtkTreeIter       *iter,
3584                  gpointer           data)
3586     char        *family,
3587         *family_escaped,
3588         *sample_escaped;
3590     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
3592     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
3594     family_escaped = g_markup_escape_text (family, -1);
3595     sample_escaped = g_markup_escape_text (sample, -1);
3597     std::stringstream markup;
3598     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
3599     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
3601     free (family);
3602     free (family_escaped);
3603     free (sample_escaped);
3606 static void delete_completion(GObject *obj, GtkWidget *entry) {
3607     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
3608     if (completion) {
3609         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
3610         g_object_unref (completion);
3611     }
3614 GtkWidget*
3615 sp_text_toolbox_new (SPDesktop *desktop)
3617     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
3619     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
3620     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
3622     GtkTooltips *tt = gtk_tooltips_new();
3623     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
3625     ////////////Family
3626     //Window
3627     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3628     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
3630     //Entry
3631     GtkWidget           *entry = gtk_entry_new ();
3632     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
3633     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
3634     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
3635     gtk_entry_completion_set_text_column (completion, 0);
3636     gtk_entry_completion_set_minimum_key_length (completion, 1);
3637     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
3638     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
3639     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
3640     aux_toolbox_space (tbl, 1);
3641     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
3642     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
3644     //Button
3645     GtkWidget   *button = gtk_button_new ();
3646     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
3647     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
3649     //Popdown
3650     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
3651     GtkWidget           *treeview = gtk_tree_view_new ();
3653     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
3654     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
3655     gtk_tree_view_column_pack_start (column, cell, FALSE);
3656     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
3657     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
3658     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
3660     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
3661     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
3662 #if GTK_CHECK_VERSION(2,6,0)
3663     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
3664 #endif // GTK_CHECK_VERSION(2,6,0)
3666     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
3668     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
3669     gtk_container_add (GTK_CONTAINER (sw), treeview);
3671     gtk_container_add (GTK_CONTAINER (window), sw);
3672     gtk_widget_set_size_request (window, 300, 450);
3674     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
3675     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
3676     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
3678     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
3680     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
3681     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
3683     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
3684     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
3686     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
3687     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
3688     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
3689     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
3690     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
3692     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
3693     aux_toolbox_space (tbl, 1);
3694     GtkWidget *box = gtk_event_box_new ();
3695     gtk_container_add (GTK_CONTAINER (box), image);
3696     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
3697     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
3698     GtkTooltips *tooltips = gtk_tooltips_new ();
3699     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
3700     gtk_widget_hide (GTK_WIDGET (box));
3701     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
3703     ////////////Size
3704     const char *sizes[] = {
3705         "4", "6", "8", "9", "10", "11", "12", "13", "14",
3706         "16", "18", "20", "22", "24", "28",
3707         "32", "36", "40", "48", "56", "64", "72", "144"
3708     };
3710     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
3711     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
3712     gtk_widget_set_size_request (cbox, 80, -1);
3713     aux_toolbox_space (tbl, 1);
3714     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
3715     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
3716     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
3717     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
3719     //spacer
3720     aux_toolbox_space (tbl, 4);
3721     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
3723     ////////////Text anchor
3724     GtkWidget *group   = gtk_radio_button_new (NULL);
3725     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
3726     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
3728     // left
3729     GtkWidget *rbutton = group;
3730     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3731     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
3732     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3734     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3735     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
3736     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
3737     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
3739     // center
3740     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3741     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3742     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
3743     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3745     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3746     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
3747     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
3748     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
3750     // right
3751     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3752     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3753     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
3754     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3756     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3757     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
3758     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
3759     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
3761     // fill
3762     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3763     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3764     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
3765     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3767     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3768     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
3769     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
3770     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
3772     aux_toolbox_space (tbl, 1);
3773     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
3775     //spacer
3776     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
3778     ////////////Text style
3779     row = gtk_hbox_new (FALSE, 4);
3781     // bold
3782     rbutton = gtk_toggle_button_new ();
3783     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3784     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
3785     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3786     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
3788     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3789     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
3790     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
3792     // italic
3793     rbutton = gtk_toggle_button_new ();
3794     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3795     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
3796     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3797     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
3799     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3800     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
3801     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
3803     aux_toolbox_space (tbl, 1);
3804     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
3806     //spacer
3807     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
3809     ////////////Text orientation
3810     group   = gtk_radio_button_new (NULL);
3811     row     = gtk_hbox_new (FALSE, 4);
3812     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
3814     // horizontal
3815     rbutton = group;
3816     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3817     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
3818     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3819     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
3821     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3822     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
3823     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
3825     // vertical
3826     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3827     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3828     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
3829     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3830     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
3832     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3833     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
3834     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
3835     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
3838     //watch selection
3839     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
3841     sigc::connection *c_selection_changed =
3842         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
3843                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
3844     pool->add_connection ("selection-changed", c_selection_changed);
3846     sigc::connection *c_selection_modified =
3847         new sigc::connection (sp_desktop_selection (desktop)->connectModified
3848                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
3849     pool->add_connection ("selection-modified", c_selection_modified);
3851     sigc::connection *c_subselection_changed =
3852         new sigc::connection (desktop->connectToolSubselectionChanged
3853                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
3854     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
3856     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
3859 #if 0
3860     // horizontal
3861     {
3862         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
3863         GtkWidget *b = group = gtk_radio_button_new (NULL);
3864         gtk_container_add (GTK_CONTAINER (b), px);
3865         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
3866         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
3867         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
3868         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
3869     }
3871     // vertical
3872     {
3873         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
3874         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3875         gtk_container_add (GTK_CONTAINER (b), px);
3876         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
3877         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
3878         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
3879         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
3880     }
3882     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
3884     // letter spacing
3885     {
3886         {
3887             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
3888             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3889             gtk_container_add (GTK_CONTAINER (hb), image);
3890             gtk_widget_show(image);
3891             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3892         }
3894         {
3895             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between letters"),
3896                                              "tools.text", "letter_spacing", 0.0,
3897                                              us, tbl, FALSE, NULL,
3898                                              -1000.0, 1000.0, 0.1, 0.1,
3899                                              sp_text_letter_changed, 0.1, 1);
3900             gtk_widget_set_size_request (hb, 45, 6);
3901             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
3902         }
3903     }
3905     // line spacing
3906     {
3907         {
3908             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
3909             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3910             gtk_container_add (GTK_CONTAINER (hb), image);
3911             gtk_widget_show(image);
3912             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3913         }
3915         {
3916             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between lines"),
3917                                              "tools.text", "line_spacing", 0,
3918                                              us, tbl, FALSE, NULL,
3919                                              -1000.0, 1000.0, 0.1, 0.1,
3920                                              sp_text_line_changed, 0.1, 1);
3921             gtk_widget_set_size_request (hb, 45, 0);
3922             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
3923         }
3924     }
3926     {
3927         // horizontal kerning/vertical kerning units menu: create
3928         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
3929         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
3930         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
3932         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
3934         // horizontal kerning
3935         {
3936             {
3937                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
3938                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3939                 gtk_container_add (GTK_CONTAINER (hb), image);
3940                 gtk_widget_show(image);
3941                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3942             }
3944             {
3945                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Horizontal kerning"),
3946                                                  "tools.text", "horizontal_kerning", 0,
3947                                                  us, tbl, FALSE, NULL,
3948                                                  -100.00, 100.00, 0.01, 0.1,
3949                                                  sp_text_horiz_kern_changed);
3950                 gtk_widget_set_size_request (hb, 45, 0);
3951                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
3952             }
3953         }
3955         // vertical kerning
3956         {
3957             {
3958                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
3959                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3960                 gtk_container_add (GTK_CONTAINER (hb), image);
3961                 gtk_widget_show(image);
3962                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3963             }
3965             {
3966                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Vertical kerning"),
3967                                                  "tools.text", "vertical_kerning", 0,
3968                                                  us, tbl, FALSE, NULL,
3969                                                  -100.00, 100.00, 0.01, 0.1,
3970                                                  sp_text_vert_kern_changed);
3971                 gtk_widget_set_size_request (hb, 45, 0);
3972                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
3973             }
3974         }
3976         // add the units menu
3977         gtk_widget_show(us);
3978         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
3979         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
3980     }
3982     // letter rotation
3983     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
3984     {
3985         {
3986             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
3987             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3988             gtk_container_add (GTK_CONTAINER (hb), image);
3989             gtk_widget_show(image);
3990             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3991         }
3992         {
3993             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Letter rotation"),
3994                                              "tools.text", "letter_rotation", 0,
3995                                              us, tbl, FALSE, NULL,
3996                                              -180.0, 180.0, 0.1, 0.1,
3997                                              sp_text_letter_rotation_changed, 0.1, 1);
3998             gtk_widget_set_size_request (hb, 45, 0);
3999             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4000         }
4001         // rotation degree label
4002         {
4003             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4004             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4005         }
4006     }
4008     // Remove Manual Kerns
4009     {
4010         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4011         GtkWidget *button = gtk_button_new ();
4012         gtk_container_add (GTK_CONTAINER (button), px);
4013         gtk_widget_show(button);
4014         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4015         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4016         gtk_widget_set_sensitive(button, TRUE);
4017         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4018     }
4019 #endif
4021     gtk_widget_show_all (tbl);
4022     return tbl;
4024 } // end of sp_text_toolbox_new()
4026 }//<unnamed> namespace
4029 //#########################
4030 //##  Connector Toolbox  ##
4031 //#########################
4033 static void sp_connector_path_set_avoid(void)
4035     cc_selection_set_avoid(true);
4039 static void sp_connector_path_set_ignore(void)
4041     cc_selection_set_avoid(false);
4046 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4048     // quit if run by the _changed callbacks
4049     if (g_object_get_data( tbl, "freeze" )) {
4050         return;
4051     }
4053     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4054     SPDocument *doc = sp_desktop_document(desktop);
4056     if (!sp_document_get_undo_sensitive(doc))
4057     {
4058         return;
4059     }
4061     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4063     if ( repr->attribute("inkscape:connector-spacing") ) {
4064         gdouble priorValue = gtk_adjustment_get_value(adj);
4065         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4066         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4067             return;
4068         }
4069     } else if ( adj->value == defaultConnSpacing ) {
4070         return;
4071     }
4073     // in turn, prevent callbacks from responding
4074     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4076     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4077     SP_OBJECT(desktop->namedview)->updateRepr();
4079     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4080     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4081         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4082         NR::Matrix m = NR::identity();
4083         avoid_item_move(&m, item);
4084     }
4086     if (items) {
4087         g_slist_free(items);
4088     }
4090     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4091             _("Change connector spacing"));
4093     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4095     spinbutton_defocus(GTK_OBJECT(tbl));
4098 static void sp_connector_graph_layout(void)
4100     if (!SP_ACTIVE_DESKTOP) return;
4102     // hack for clones, see comment in align-and-distribute.cpp
4103     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4104     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4106     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4108     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4110     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4113 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4115     if ( gtk_toggle_action_get_active( act ) ) {
4116         prefs_set_string_attribute("tools.connector", "directedlayout",
4117                 "true");
4118     } else {
4119         prefs_set_string_attribute("tools.connector", "directedlayout",
4120                 "false");
4121     }
4124 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject *tbl )
4126     if ( gtk_toggle_action_get_active( act ) ) {
4127         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4128                 "true");
4129     } else {
4130         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4131                 "false");
4132     }
4136 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4138     prefs_set_double_attribute("tools.connector", "length", adj->value);
4139     spinbutton_defocus(GTK_OBJECT(tbl));
4142 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4143         gchar const *name, gchar const *old_value, gchar const *new_value,
4144         bool is_interactive, gpointer data)
4146     GtkWidget *tbl = GTK_WIDGET(data);
4148     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4149         return;
4150     }
4151     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4152         return;
4153     }
4155     GtkAdjustment *adj = (GtkAdjustment*)
4156             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4157     gdouble spacing = defaultConnSpacing;
4158     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4160     gtk_adjustment_set_value(adj, spacing);
4164 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4165     NULL, /* child_added */
4166     NULL, /* child_removed */
4167     connector_tb_event_attr_changed,
4168     NULL, /* content_changed */
4169     NULL  /* order_changed */
4170 };
4173 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4175     {
4176         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4177                                           _("Avoid"),
4178                                           _("Make connectors avoid selected objects"),
4179                                           "connector_avoid",
4180                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4181         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
4182         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4183     }
4185     {
4186         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
4187                                           _("Ignore"),
4188                                           _("Make connectors ignore selected objects"),
4189                                           "connector_ignore",
4190                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4191         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
4192         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4193     }
4195     EgeAdjustmentAction* eact = 0;
4197     // Spacing spinbox
4198     eact = create_adjustment_action( "ConnectorSpacingAction",
4199                                      _("Spacing:"), _("The amount of space left around objects by auto-routing connectors"),
4200                                      "tools.connector", "spacing", defaultConnSpacing,
4201                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
4202                                      0, 100, 1.0, 10.0,
4203                                      0, 0, 0,
4204                                      connector_spacing_changed, 1, 0 );
4205     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4207     // Graph (connector network) layout
4208     {
4209         InkAction* inky = ink_action_new( "ConnectorGraphAction",
4210                                           _("Graph"),
4211                                           _("Nicely arrange selected connector network"),
4212                                           "graph_layout",
4213                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4214         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
4215         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4216     }
4218     // Default connector length spinbox
4219     eact = create_adjustment_action( "ConnectorLengthAction",
4220                                      _("Length:"), _("Ideal length for connectors when layout is applied"),
4221                                      "tools.connector", "length", 100,
4222                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
4223                                      10, 1000, 10.0, 100.0,
4224                                      0, 0, 0,
4225                                      connector_length_changed, 1, 0 );
4226     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4229     // Directed edges toggle button
4230     {
4231         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
4232                                                       _("Downwards"),
4233                                                       _("Make connectors with end-markers (arrows) point downwards"),
4234                                                       "directed_graph",
4235                                                       Inkscape::ICON_SIZE_DECORATION );
4236         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4238         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
4239         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4240                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4242         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
4243     }
4245     // Avoid overlaps toggle button
4246     {
4247         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
4248                                                       _("Remove overlaps"),
4249                                                       _("Do not allow overlapping shapes"),
4250                                                       "remove_overlaps",
4251                                                       Inkscape::ICON_SIZE_DECORATION );
4252         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4254         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
4255         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4256                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
4258         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
4259     }
4261     // Code to watch for changes to the connector-spacing attribute in
4262     // the XML.
4263     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4264     g_assert(repr != NULL);
4266     purge_repr_listener( holder, holder );
4268     if (repr) {
4269         g_object_set_data( holder, "repr", repr );
4270         Inkscape::GC::anchor(repr);
4271         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
4272         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
4273     }
4274 } // end of sp_connector_toolbox_prep()
4276 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* tbl)
4278     gint channels = ege_select_one_action_get_active( act );
4279     flood_channels_set_channels( channels );
4282 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject *tbl)
4284     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4287 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
4289     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
4290     SPUnit const *unit = tracker->getActiveUnit();
4293     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4296 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
4298     // FIXME: make defaults settable via Inkscape Options
4299     struct KeyValue {
4300         char const *key;
4301         double value;
4302     } const key_values[] = {
4303         {"threshold", 15},
4304         {"offset", 0.0}
4305     };
4307     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
4308         KeyValue const &kv = key_values[i];
4309         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
4310         if ( adj ) {
4311             gtk_adjustment_set_value(adj, kv.value);
4312         }
4313     }
4314     
4315     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
4316     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
4319 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4321     EgeAdjustmentAction* eact = 0;
4323     {
4324         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4326         GList* items = 0;
4327         gint count = 0;
4328         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
4329         {
4330             GtkTreeIter iter;
4331             gtk_list_store_append( model, &iter );
4332             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4333             count++;
4334         }
4335         g_list_free( items );
4336         items = 0;
4337         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by:"), _(""), NULL, GTK_TREE_MODEL(model) );
4338         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
4339         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
4340         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4341         g_object_set_data( holder, "channels_action", act1 );
4342     }
4344     // Spacing spinbox
4345     {
4346         eact = create_adjustment_action(
4347             "ThresholdAction",
4348             _("Threshold:"),
4349             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
4350             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
4351             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
4352             0, 0, 0,
4353             paintbucket_threshold_changed, 1, 0 );
4355         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4356     }
4358     // Create the units menu.
4359     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
4360     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
4361     g_object_set_data( holder, "tracker", tracker );
4362     {
4363         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), _("") );
4364         gtk_action_group_add_action( mainActions, act );
4365     }
4367     // Offset spinbox
4368     {
4369         eact = create_adjustment_action(
4370             "OffsetAction",
4371             _("Grow/shrink by:"),
4372             _("The amount to grow (positive) or shrink (negative) the created fill path"),
4373             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
4374             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
4375             0, 0, 0,
4376             paintbucket_offset_changed, 1, 2);
4377         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4379         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4380     }
4381     
4382     /* Reset */
4383     {
4384         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
4385                                           _("Defaults"),
4386                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4387                                           GTK_STOCK_CLEAR );
4388         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
4389         gtk_action_group_add_action( mainActions, act );
4390         gtk_action_set_sensitive( act, TRUE );
4391     }
4395 /*
4396   Local Variables:
4397   mode:c++
4398   c-file-style:"stroustrup"
4399   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4400   indent-tabs-mode:nil
4401   fill-column:99
4402   End:
4403 */
4404 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :