Code

Refactored toolbar creation
[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 GtkWidget *sp_dropper_toolbox_new(SPDesktop *desktop);
109 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
110 static GtkWidget *sp_connector_toolbox_new(SPDesktop *desktop);
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", sp_dropper_toolbox_new, 0,          0,
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", sp_connector_toolbox_new, 0,    0,
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         "  </toolbar>"
328         "</ui>"
331 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
333 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
335 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
336 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
338 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
339 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
341 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
342 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
344 /* Global text entry widgets necessary for update */
345 /* GtkWidget *dropper_rgb_entry,
346           *dropper_opacity_entry ; */
347 // should be made a private member once this is converted to class
349 static void delete_connection(GObject *obj, sigc::connection *connection) {
350     connection->disconnect();
351     delete connection;
354 static GtkWidget *
355 sp_toolbox_button_new(GtkWidget *t, Inkscape::IconSize size, gchar const *pxname, GtkSignalFunc handler,
356                       GtkTooltips *tt, gchar const *tip)
358     GtkWidget *b = sp_button_new_from_data(size, SP_BUTTON_TYPE_NORMAL, NULL, pxname, tip, tt);
359     gtk_widget_show(b);
360     if (handler) gtk_signal_connect(GTK_OBJECT(b), "clicked", handler, NULL);
361     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
363     return b;
366 GtkWidget *
367 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
368                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
369                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
371     SPAction *action = verb->get_action(view);
372     if (!action) return NULL;
374     SPAction *doubleclick_action;
375     if (doubleclick_verb)
376         doubleclick_action = doubleclick_verb->get_action(view);
377     else
378         doubleclick_action = NULL;
380     /* fixme: Handle sensitive/unsensitive */
381     /* fixme: Implement sp_button_new_from_action */
382     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
383     gtk_widget_show(b);
384     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
386     return b;
389 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
390                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
392     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
395 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
396                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
398     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
402 static void trigger_sp_action( GtkAction* act, gpointer user_data )
404     SPAction* targetAction = SP_ACTION(user_data);
405     if ( targetAction ) {
406         sp_action_perform( targetAction, NULL );
407     }
410 static void sp_action_action_set_sensitive (SPAction *action, unsigned int sensitive, void *data)
412     if ( data ) {
413         GtkAction* act = GTK_ACTION(data);
414         gtk_action_set_sensitive( act, sensitive );
415     }
418 static SPActionEventVector action_event_vector = {
419     {NULL},
420     NULL,
421     NULL,
422     sp_action_action_set_sensitive,
423     NULL,
424     NULL
425 };
427 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
429     GtkAction* act = 0;
431     SPAction* targetAction = verb->get_action(view);
432     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
433     act = GTK_ACTION(inky);
434     gtk_action_set_sensitive( act, targetAction->sensitive );
436     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
438     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
439     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
441     return act;
444 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
446     Inkscape::UI::View::View *view = desktop;
447     gint verbsToUse[] = {
448         // disabled until we have icons for them:
449         //find
450         //SP_VERB_EDIT_TILE,
451         //SP_VERB_EDIT_UNTILE,
452         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
453         SP_VERB_DIALOG_DISPLAY,
454         SP_VERB_DIALOG_FILL_STROKE,
455         SP_VERB_DIALOG_NAMEDVIEW,
456         SP_VERB_DIALOG_TEXT,
457         SP_VERB_DIALOG_XML_EDITOR,
458         SP_VERB_EDIT_CLONE,
459         SP_VERB_EDIT_COPY,
460         SP_VERB_EDIT_CUT,
461         SP_VERB_EDIT_DUPLICATE,
462         SP_VERB_EDIT_PASTE,
463         SP_VERB_EDIT_REDO,
464         SP_VERB_EDIT_UNDO,
465         SP_VERB_EDIT_UNLINK_CLONE,
466         SP_VERB_FILE_EXPORT,
467         SP_VERB_FILE_IMPORT,
468         SP_VERB_FILE_NEW,
469         SP_VERB_FILE_OPEN,
470         SP_VERB_FILE_PRINT,
471         SP_VERB_FILE_SAVE,
472         SP_VERB_OBJECT_TO_CURVE,
473         SP_VERB_SELECTION_GROUP,
474         SP_VERB_SELECTION_OUTLINE,
475         SP_VERB_SELECTION_UNGROUP,
476         SP_VERB_ZOOM_1_1,
477         SP_VERB_ZOOM_1_2,
478         SP_VERB_ZOOM_2_1,
479         SP_VERB_ZOOM_DRAWING,
480         SP_VERB_ZOOM_IN,
481         SP_VERB_ZOOM_NEXT,
482         SP_VERB_ZOOM_OUT,
483         SP_VERB_ZOOM_PAGE,
484         SP_VERB_ZOOM_PAGE_WIDTH,
485         SP_VERB_ZOOM_PREV,
486         SP_VERB_ZOOM_SELECTION,
487     };
489     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
490     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
492     static std::map<SPDesktop*, GtkActionGroup*> groups;
493     GtkActionGroup* mainActions = 0;
494     if ( groups.find(desktop) != groups.end() ) {
495         mainActions = groups[desktop];
496     }
498     if ( !mainActions ) {
499         mainActions = gtk_action_group_new("main");
500         groups[desktop] = mainActions;
501     }
503     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
504         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
505         if ( verb ) {
506             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
507                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
508                 gtk_action_group_add_action( mainActions, act );
509             }
510         }
511     }
513     return mainActions;
517 GtkWidget *
518 sp_tool_toolbox_new()
520     GtkTooltips *tt = gtk_tooltips_new();
521     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
523     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
524     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
526     gtk_widget_set_sensitive(tb, FALSE);
528     GtkWidget *hb = gtk_handle_box_new();
529     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
530     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
531     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
533     gtk_container_add(GTK_CONTAINER(hb), tb);
534     gtk_widget_show(GTK_WIDGET(tb));
536     sigc::connection* conn = new sigc::connection;
537     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
539     return hb;
542 static void
543 aux_toolbox_attached(GtkHandleBox *toolbox, GtkWidget *child)
545     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
546     gtk_widget_queue_resize(child);
549 static void
550 aux_toolbox_detached(GtkHandleBox *toolbox, GtkWidget *child)
552     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
553     gtk_widget_queue_resize(child);
556 GtkWidget *
557 sp_aux_toolbox_new()
559     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
561     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
562     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
563     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
564     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
565     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
567     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
568     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
570     gtk_widget_set_sensitive(tb, FALSE);
572     GtkWidget *hb = gtk_handle_box_new();
573     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
574     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
575     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
577     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
578     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
580     gtk_container_add(GTK_CONTAINER(hb), tb);
581     gtk_widget_show(GTK_WIDGET(tb));
583     sigc::connection* conn = new sigc::connection;
584     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
586     return hb;
589 //####################################
590 //# Commands Bar
591 //####################################
593 GtkWidget *
594 sp_commands_toolbox_new()
596     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
598     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
599     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
600     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
601     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
602     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
604     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
605     gtk_widget_set_sensitive(tb, FALSE);
607     GtkWidget *hb = gtk_handle_box_new();
608     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
609     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
610     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
612     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
613     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
615     gtk_container_add(GTK_CONTAINER(hb), tb);
616     gtk_widget_show(GTK_WIDGET(tb));
618     sigc::connection* conn = new sigc::connection;
619     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
621     return hb;
625 //####################################
626 //# node editing callbacks
627 //####################################
629 void
630 sp_node_path_edit_add(void)
632     sp_node_selected_add_node();
635 void
636 sp_node_path_edit_delete(void)
638     sp_node_selected_delete();
641 void
642 sp_node_path_edit_delete_segment(void)
644     sp_node_selected_delete_segment();
647 void
648 sp_node_path_edit_break(void)
650     sp_node_selected_break();
653 void
654 sp_node_path_edit_join(void)
656     sp_node_selected_join();
659 void
660 sp_node_path_edit_join_segment(void)
662     sp_node_selected_join_segment();
665 void
666 sp_node_path_edit_toline(void)
668     sp_node_selected_set_line_type(NR_LINETO);
671 void
672 sp_node_path_edit_tocurve(void)
674     sp_node_selected_set_line_type(NR_CURVETO);
677 void
678 sp_node_path_edit_cusp(void)
680     sp_node_selected_set_type(Inkscape::NodePath::NODE_CUSP);
683 void
684 sp_node_path_edit_smooth(void)
686     sp_node_selected_set_type(Inkscape::NodePath::NODE_SMOOTH);
689 void
690 sp_node_path_edit_symmetrical(void)
692     sp_node_selected_set_type(Inkscape::NodePath::NODE_SYMM);
695 static void toggle_show_handles (GtkToggleAction *act, gpointer data) {
696     bool show = gtk_toggle_action_get_active( act );
697     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
698     sp_nodepath_show_handles(show);
701 //################################
702 //##    Node Editing Toolbox    ##
703 //################################
705 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
707     {
708         InkAction* inky = ink_action_new( "NodeInsertAction",
709                                           _("Insert"),
710                                           _("Insert new nodes into selected segments"),
711                                           "node_insert",
712                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
713         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
714         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
715     }
717     {
718         InkAction* inky = ink_action_new( "NodeDeleteAction",
719                                           _("Delete"),
720                                           _("Delete selected nodes"),
721                                           "node_delete",
722                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
723         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
724         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
725     }
727     {
728         InkAction* inky = ink_action_new( "NodeJoinAction",
729                                           _("Join"),
730                                           _("Join selected endnodes"),
731                                           "node_join",
732                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
733         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
734         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
735     }
737     {
738         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
739                                           _("Join Segment"),
740                                           _("Join selected endnodes with a new segment"),
741                                           "node_join_segment",
742                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
743         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
744         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
745     }
747     {
748         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
749                                           _("Delete Segment"),
750                                           _("Split path between two non-endpoint nodes"),
751                                           "node_delete_segment",
752                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
753         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
754         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
755     }
757     {
758         InkAction* inky = ink_action_new( "NodeBreakAction",
759                                           _("Node Break"),
760                                           _("Break path at selected nodes"),
761                                           "node_break",
762                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
763         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
764         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
765     }
767     {
768         InkAction* inky = ink_action_new( "NodeCuspAction",
769                                           _("Node Cusp"),
770                                           _("Make selected nodes corner"),
771                                           "node_cusp",
772                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
773         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
774         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
775     }
777     {
778         InkAction* inky = ink_action_new( "NodeSmoothAction",
779                                           _("Node Smooth"),
780                                           _("Make selected nodes smooth"),
781                                           "node_smooth",
782                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
783         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
784         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
785     }
787     {
788         InkAction* inky = ink_action_new( "NodeSymmetricAction",
789                                           _("Node Symmetric"),
790                                           _("Make selected nodes symmetric"),
791                                           "node_symmetric",
792                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
793         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
794         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
795     }
797     {
798         InkAction* inky = ink_action_new( "NodeLineAction",
799                                           _("Node Line"),
800                                           _("Make selected segments lines"),
801                                           "node_line",
802                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
803         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
804         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
805     }
807     {
808         InkAction* inky = ink_action_new( "NodeCurveAction",
809                                           _("Node Curve"),
810                                           _("Make selected segments curves"),
811                                           "node_curve",
812                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
813         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
814         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
815     }
817     {
818         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
819                                                       _("Show Handles"),
820                                                       _("Show the Bezier handles of selected nodes"),
821                                                       "nodes_show_handles",
822                                                       Inkscape::ICON_SIZE_DECORATION );
823         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
824         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
825         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
826     }
828 } // end of sp_node_toolbox_prep()
831 //########################
832 //##    Zoom Toolbox    ##
833 //########################
835 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
837     // no custom GtkAction setup needed
838 } // end of sp_zoom_toolbox_prep()
840 void
841 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
843     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")));
847 void
848 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
850     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")));
853 void
854 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
856     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")));
859 static void
860 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
862     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
863     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
865     if (old_desktop) {
866         GList *children, *iter;
868         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
869         for ( iter = children ; iter ; iter = iter->next ) {
870             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
871         }
872         g_list_free(children);
873     }
875     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
877     if (desktop) {
878         gtk_widget_set_sensitive(toolbox, TRUE);
879         setup_func(toolbox, desktop);
880         update_func(desktop, desktop->event_context, toolbox);
881         *conn = desktop->connectEventContextChanged
882             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
883     } else {
884         gtk_widget_set_sensitive(toolbox, FALSE);
885     }
887 } // end of toolbox_set_desktop()
890 static void
891 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
893     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
894     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
895     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
897     for (int i = 0 ; tools[i].type_name ; i++ ) {
898         GtkWidget *button =
899             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
900                                                               SP_BUTTON_TYPE_TOGGLE,
901                                                               Inkscape::Verb::get(tools[i].verb),
902                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
903                                                               desktop,
904                                                               tooltips );
906         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
907                            (gpointer)button );
908     }
912 static void
913 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
915     gchar const *const tname = ( eventcontext
916                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
917                                  : NULL );
918     for (int i = 0 ; tools[i].type_name ; i++ ) {
919         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
920         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
921     }
924 static void
925 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
927     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
928     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
929     GtkUIManager* mgr = gtk_ui_manager_new();
930     GError* errVal = 0;
931     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
932     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
934     std::map<std::string, GtkWidget*> dataHolders;
936     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
937         if ( aux_toolboxes[i].prep_func ) {
938             // converted to GtkActions and UIManager
940             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
941             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
942             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
943             dataHolders[aux_toolboxes[i].type_name] = kludge;
944             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
945         } else {
947             GtkWidget *sub_toolbox = 0;
948             if (aux_toolboxes[i].create_func == NULL)
949                 sub_toolbox = sp_empty_toolbox_new(desktop);
950             else {
951                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
952             }
954             gtk_size_group_add_widget( grouper, sub_toolbox );
956             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
957             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
959         }
960     }
962     // Second pass to create toolbars *after* all GtkActions are created
963     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
964         if ( aux_toolboxes[i].prep_func ) {
965             // converted to GtkActions and UIManager
967             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
969             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
970             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
972             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
973             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
974             g_free( tmp );
975             tmp = 0;
977             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
978             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
979             gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
980             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
983             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
985             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
986                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
987                 swatch->setDesktop( desktop );
988                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
989                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
990                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
991                 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 );
992             }
994             gtk_widget_show_all( holder );
995             sp_set_font_size_smaller( holder );
997             gtk_size_group_add_widget( grouper, holder );
999             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1000             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1001         }
1002     }
1004     g_object_unref( G_OBJECT(grouper) );
1007 static void
1008 update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1010     gchar const *tname = ( eventcontext
1011                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1012                            : NULL );
1013     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1014         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1015         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1016             gtk_widget_show_all(sub_toolbox);
1017             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1018         } else {
1019             gtk_widget_hide(sub_toolbox);
1020         }
1021     }
1024 static void
1025 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1027     gchar const * descr =
1028         "<ui>"
1029         "  <toolbar name='CommandsToolbar'>"
1030         "    <toolitem action='FileNew' />"
1031         "    <toolitem action='FileOpen' />"
1032         "    <toolitem action='FileSave' />"
1033         "    <toolitem action='FilePrint' />"
1034         "    <separator />"
1035         "    <toolitem action='FileImport' />"
1036         "    <toolitem action='FileExport' />"
1037         "    <separator />"
1038         "    <toolitem action='EditUndo' />"
1039         "    <toolitem action='EditRedo' />"
1040         "    <separator />"
1041         "    <toolitem action='EditCopy' />"
1042         "    <toolitem action='EditCut' />"
1043         "    <toolitem action='EditPaste' />"
1044         "    <separator />"
1045         "    <toolitem action='ZoomSelection' />"
1046         "    <toolitem action='ZoomDrawing' />"
1047         "    <toolitem action='ZoomPage' />"
1048         "    <separator />"
1049         "    <toolitem action='EditDuplicate' />"
1050         "    <toolitem action='EditClone' />"
1051         "    <toolitem action='EditUnlinkClone' />"
1052         "    <separator />"
1053         "    <toolitem action='SelectionGroup' />"
1054         "    <toolitem action='SelectionUnGroup' />"
1055         "    <separator />"
1056         "    <toolitem action='DialogFillStroke' />"
1057         "    <toolitem action='DialogText' />"
1058         "    <toolitem action='DialogXMLEditor' />"
1059         "    <toolitem action='DialogAlignDistribute' />"
1060         "    <separator />"
1061         "    <toolitem action='DialogPreferences' />"
1062         "    <toolitem action='DialogDocumentProperties' />"
1063         "  </toolbar>"
1064         "</ui>";
1065     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1068     GtkUIManager* mgr = gtk_ui_manager_new();
1069     GError* errVal = 0;
1071     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1072     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1074     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1075     gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1076     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1077     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1078     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1081     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1084 static void
1085 update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
1089 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1091     gtk_widget_show(toolbox_toplevel);
1092     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1094     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1095     if (!shown_toolbox) {
1096         return;
1097     }
1098     gtk_widget_show(toolbox);
1100     // need to show the spacer, or the padding will be off
1101     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1102     gtk_widget_show(spacer);
1104     gtk_widget_show_all(shown_toolbox);
1107 void
1108 aux_toolbox_space(GtkWidget *tb, gint space)
1110     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1113 static GtkWidget *
1114 sp_empty_toolbox_new(SPDesktop *desktop)
1116     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1117     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1118     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1120     gtk_widget_show_all(tbl);
1121     sp_set_font_size_smaller (tbl);
1123     return tbl;
1126 // helper UI functions
1128 GtkWidget *
1129 sp_tb_spinbutton(
1130     gchar *label, gchar const *tooltip,
1131     gchar const *path, gchar const *data, gdouble def,
1132     GtkWidget *us,
1133     GtkWidget *tbl,
1134     gboolean altx, gchar const *altx_mark,
1135     gdouble lower, gdouble upper, gdouble step, gdouble page,
1136     void (*callback)(GtkAdjustment *, GtkWidget *),
1137     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1139     GtkTooltips *tt = gtk_tooltips_new();
1141     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1143     GtkWidget *l = gtk_label_new(label);
1144     gtk_widget_show(l);
1145     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1146     gtk_container_add(GTK_CONTAINER(hb), l);
1148     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1149                                       lower, upper, step, page, page);
1150     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1151     if (us)
1152         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1154     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1155     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1156     if (altx)
1157         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1158     gtk_widget_set_size_request(sb,
1159                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1160                                 AUX_SPINBUTTON_HEIGHT);
1161     gtk_widget_show(sb);
1162     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1163     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1164     gtk_container_add(GTK_CONTAINER(hb), sb);
1165     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1167     return hb;
1170 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1171                                                        gchar const *label, gchar const *tooltip,
1172                                                        gchar const *path, gchar const *data, gdouble def,
1173                                                        GtkWidget *focusTarget,
1174                                                        GtkWidget *us,
1175                                                        GObject *dataKludge,
1176                                                        gboolean altx, gchar const *altx_mark,
1177                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1178                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1179                                                        void (*callback)(GtkAdjustment *, GObject *),
1180                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1182     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
1183                                                              lower, upper, step, page, page ) );
1184     if (us) {
1185         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1186     }
1188     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1190     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1192     if ( (descrCount > 0) && descrLabels && descrValues ) {
1193         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1194     }
1196     if ( focusTarget ) {
1197         ege_adjustment_action_set_focuswidget( act, focusTarget );
1198     }
1200     if ( altx && altx_mark ) {
1201         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1202     }
1204     if ( dataKludge ) {
1205         g_object_set_data( dataKludge, data, adj );
1206     }
1208     // Using a cast just to make sure we pass in the right kind of function pointer
1209     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1211     return act;
1215 #define MODE_LABEL_WIDTH 70
1217 //########################
1218 //##       Star         ##
1219 //########################
1221 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1223     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1225     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1226         // do not remember prefs if this call is initiated by an undo change, because undoing object
1227         // creation sets bogus values to its attributes before it is deleted
1228         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1229     }
1231     // quit if run by the attr_changed listener
1232     if (g_object_get_data( dataKludge, "freeze" )) {
1233         return;
1234     }
1236     // in turn, prevent listener from responding
1237     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1239     bool modmade = false;
1241     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1242     GSList const *items = selection->itemList();
1243     for (; items != NULL; items = items->next) {
1244         if (SP_IS_STAR((SPItem *) items->data)) {
1245             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1246             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1247             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1248                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1249                                     + M_PI / (gint)adj->value));
1250             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1251             modmade = true;
1252         }
1253     }
1254     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1255                                    _("Star: Change number of corners"));
1257     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1260 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1262     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1264     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1265         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1266     }
1268     // quit if run by the attr_changed listener
1269     if (g_object_get_data( dataKludge, "freeze" )) {
1270         return;
1271     }
1273     // in turn, prevent listener from responding
1274     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1276     bool modmade = false;
1277     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1278     GSList const *items = selection->itemList();
1279     for (; items != NULL; items = items->next) {
1280         if (SP_IS_STAR((SPItem *) items->data)) {
1281             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1283             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1284             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1285             if (r2 < r1) {
1286                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1287             } else {
1288                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1289             }
1291             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1292             modmade = true;
1293         }
1294     }
1296     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1297                                    _("Star: Change spoke ratio"));
1299     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1302 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1304     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1305     bool flat = ege_select_one_action_get_active( act ) == 0;
1307     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1308         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1309                                     flat ? "true" : "false" );
1310     }
1312     // quit if run by the attr_changed listener
1313     if (g_object_get_data( dataKludge, "freeze" )) {
1314         return;
1315     }
1317     // in turn, prevent listener from responding
1318     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1320     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1321     GSList const *items = selection->itemList();
1322     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1323     bool modmade = false;
1325     if ( prop_action ) {
1326         gtk_action_set_sensitive( prop_action, !flat );
1327     }
1329     for (; items != NULL; items = items->next) {
1330         if (SP_IS_STAR((SPItem *) items->data)) {
1331             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1332             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1333             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1334             modmade = true;
1335         }
1336     }
1338     if (modmade) {
1339         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1340                          flat ? _("Make polygon") : _("Make star"));
1341     }
1343     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1346 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1348     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1350     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1351         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1352     }
1354     // quit if run by the attr_changed listener
1355     if (g_object_get_data( dataKludge, "freeze" )) {
1356         return;
1357     }
1359     // in turn, prevent listener from responding
1360     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1362     bool modmade = false;
1364     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1365     GSList const *items = selection->itemList();
1366     for (; items != NULL; items = items->next) {
1367         if (SP_IS_STAR((SPItem *) items->data)) {
1368             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1369             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1370             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1371             modmade = true;
1372         }
1373     }
1374     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1375                                    _("Star: Change rounding"));
1377     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1380 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1382     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1384     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1385         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1386     }
1388     // quit if run by the attr_changed listener
1389     if (g_object_get_data( dataKludge, "freeze" )) {
1390         return;
1391     }
1393     // in turn, prevent listener from responding
1394     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1396     bool modmade = false;
1398     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1399     GSList const *items = selection->itemList();
1400     for (; items != NULL; items = items->next) {
1401         if (SP_IS_STAR((SPItem *) items->data)) {
1402             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1403             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1404             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1405             modmade = true;
1406         }
1407     }
1408     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1409                                    _("Star: Change randomization"));
1411     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1415 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1416                                        gchar const *old_value, gchar const *new_value,
1417                                        bool is_interactive, gpointer data)
1419     GtkWidget *tbl = GTK_WIDGET(data);
1421     // quit if run by the _changed callbacks
1422     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1423         return;
1424     }
1426     // in turn, prevent callbacks from responding
1427     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1429     GtkAdjustment *adj = 0;
1431     if (!strcmp(name, "inkscape:randomized")) {
1432         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1433         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1434     } else if (!strcmp(name, "inkscape:rounded")) {
1435         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1436         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1437     } else if (!strcmp(name, "inkscape:flatsided")) {
1438         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1439         char const *flatsides = repr->attribute("inkscape:flatsided");
1440         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1441         if ( flatsides && !strcmp(flatsides,"false") ) {
1442             ege_select_one_action_set_active( flat_action, 1 );
1443             gtk_action_set_sensitive( prop_action, TRUE );
1444         } else {
1445             ege_select_one_action_set_active( flat_action, 0 );
1446             gtk_action_set_sensitive( prop_action, FALSE );
1447         }
1448     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1449         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1450         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1451         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1452         if (r2 < r1) {
1453             gtk_adjustment_set_value(adj, r2/r1);
1454         } else {
1455             gtk_adjustment_set_value(adj, r1/r2);
1456         }
1457     } else if (!strcmp(name, "sodipodi:sides")) {
1458         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1459         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1460     }
1462     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1466 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1468     NULL, /* child_added */
1469     NULL, /* child_removed */
1470     star_tb_event_attr_changed,
1471     NULL, /* content_changed */
1472     NULL  /* order_changed */
1473 };
1476 /**
1477  *  \param selection Should not be NULL.
1478  */
1479 static void
1480 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1482     int n_selected = 0;
1483     Inkscape::XML::Node *repr = NULL;
1484     Inkscape::XML::Node *oldrepr = NULL;
1486     for (GSList const *items = selection->itemList();
1487          items != NULL;
1488          items = items->next)
1489     {
1490         if (SP_IS_STAR((SPItem *) items->data)) {
1491             n_selected++;
1492             repr = SP_OBJECT_REPR((SPItem *) items->data);
1493         }
1494     }
1496     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1498     if (n_selected == 0) {
1499         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1500     } else if (n_selected == 1) {
1501         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1503         oldrepr = (Inkscape::XML::Node *) g_object_get_data( tbl, "repr" );
1504         if (oldrepr) { // remove old listener
1505             sp_repr_remove_listener_by_data(oldrepr, tbl);
1506             Inkscape::GC::release(oldrepr);
1507             oldrepr = 0;
1508             g_object_set_data( tbl, "repr", NULL );
1509         }
1511         if (repr) {
1512             g_object_set_data( tbl, "repr", repr );
1513             Inkscape::GC::anchor(repr);
1514             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1515             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1516         }
1517     } else {
1518         // FIXME: implement averaging of all parameters for multiple selected stars
1519         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1520         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1521     }
1525 static void sp_stb_defaults( GtkWidget *widget, GObject *dataKludge )
1527     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1528     // callbacks to lump all the changes for all selected objects in one undo step
1530     GtkAdjustment *adj = 0;
1532     // fixme: make settable in prefs!
1533     gint mag = 5;
1534     gdouble prop = 0.5;
1535     gboolean flat = FALSE;
1536     gdouble randomized = 0;
1537     gdouble rounded = 0;
1539     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1540     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1542     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1543     gtk_action_set_sensitive( sb2, !flat );
1545     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1546     gtk_adjustment_set_value(adj, mag);
1547     gtk_adjustment_value_changed(adj);
1549     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1550     gtk_adjustment_set_value(adj, prop);
1551     gtk_adjustment_value_changed(adj);
1553     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1554     gtk_adjustment_set_value(adj, rounded);
1555     gtk_adjustment_value_changed(adj);
1557     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1558     gtk_adjustment_set_value(adj, randomized);
1559     gtk_adjustment_value_changed(adj);
1563 void
1564 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1566     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1567     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1568     GtkWidget *l = gtk_label_new(NULL);
1569     gtk_label_set_markup(GTK_LABEL(l), title);
1570     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1571     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1572     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1576 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1578     {
1579         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1580         ege_output_action_set_use_markup( act, TRUE );
1581         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1582         g_object_set_data( holder, "mode_action", act );
1583     }
1585     {
1586         //EgeAdjustmentAction* calligraphy_angle = 0;
1587         EgeAdjustmentAction* eact = 0;
1588         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1589         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1591         /* Flatsided checkbox */
1592         {
1593             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1595             GtkTreeIter iter;
1596             gtk_list_store_append( model, &iter );
1597             gtk_list_store_set( model, &iter,
1598                                 0, _("Polygon"),
1599                                 1, _("Regular polygon (with one handle) instead of a star"),
1600                                 2, "star_flat",
1601                                 -1 );
1603             gtk_list_store_append( model, &iter );
1604             gtk_list_store_set( model, &iter,
1605                                 0, _("Star"),
1606                                 1, _("Star instead of a regular polygon (with one handle)"),
1607                                 2, "star_angled",
1608                                 -1 );
1610             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
1611             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1612             g_object_set_data( holder, "flat_action", act );
1614             ege_select_one_action_set_appearance( act, "full" );
1615             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1616             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1617             ege_select_one_action_set_icon_column( act, 2 );
1618             ege_select_one_action_set_tooltip_column( act, 1  );
1620             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1621             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1622         }
1624         /* Magnitude */
1625         //gchar const* labels[] = {_("tri (default)"), _("quad"), _("pent")};
1626         //gdouble values[] = {3, 4, 5};
1627         eact = create_adjustment_action( "MagnitudeAction",
1628                                          _("Corners:"), _("Number of corners of a polygon or star"),
1629                                          "tools.shapes.star", "magnitude", 3,
1630                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1631                                          3, 1024, 1, 5,
1632                                          0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
1633                                          sp_stb_magnitude_value_changed,
1634                                          1.0, 0 );
1635         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1636         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1638         /* Spoke ratio */
1639         //gchar const* labels2[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1640         //gdouble values2[] = {-90, 0, 30, 90};
1641         eact = create_adjustment_action( "SpokeAction",
1642                                          _("Spoke ratio:"),
1643                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1644                                          // Base radius is the same for the closest handle.
1645                                          _("Base radius to tip radius ratio"),
1646                                          "tools.shapes.star", "proportion", 0.5,
1647                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1648                                          0.01, 1.0, 0.01, 0.1,
1649                                          0, 0, 0, // labels2, values2, G_N_ELEMENTS(labels2),
1650                                          sp_stb_proportion_value_changed );
1651         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1652         g_object_set_data( holder, "prop_action", eact );
1654         if ( !isFlatSided ) {
1655             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1656         } else {
1657             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1658         }
1660         /* Roundedness */
1661         //gchar const* labels3[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1662         //gdouble values3[] = {-90, 0, 30, 90};
1663         eact = create_adjustment_action( "RoundednessAction",
1664                                          _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1665                                          "tools.shapes.star", "rounded", 0.0,
1666                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1667                                          -100.0, 100.0, 0.01, 0.1,
1668                                          0, 0, 0, // labels3, values3, G_N_ELEMENTS(labels3),
1669                                          sp_stb_rounded_value_changed );
1670         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1671         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1673         /* Randomization */
1674         //gchar const* labels4[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1675         //gdouble values4[] = {-90, 0, 30, 90};
1676         eact = create_adjustment_action( "RandomizationAction",
1677                                          _("Randomized:"), _("Scatter randomly the corners and angles"),
1678                                          "tools.shapes.star", "randomized", 0.0,
1679                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1680                                          -10.0, 10.0, 0.001, 0.01,
1681                                          0, 0, 0, // labels4, values4, G_N_ELEMENTS(labels4),
1682                                          sp_stb_randomized_value_changed, 0.1, 3 );
1683         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1684         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1685     }
1687     {
1688         /* Reset */
1689         {
1690             GtkAction* act = gtk_action_new( "StarResetAction",
1691                                              _("Defaults"),
1692                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1693                                              GTK_STOCK_CLEAR );
1694             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1695             gtk_action_group_add_action( mainActions, act );
1696             gtk_action_set_sensitive( act, TRUE );
1697         }
1698     }
1700     sigc::connection *connection = new sigc::connection(
1701         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1702         );
1703     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1707 //########################
1708 //##       Rect         ##
1709 //########################
1711 static void sp_rtb_sensitivize( GObject *tbl )
1713     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1714     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1715     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1717     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1718         gtk_action_set_sensitive( not_rounded, FALSE );
1719     } else {
1720         gtk_action_set_sensitive( not_rounded, TRUE );
1721     }
1725 static void
1726 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1727                           void (*setter)(SPRect *, gdouble))
1729     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1731     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1732     SPUnit const *unit = tracker->getActiveUnit();
1734     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1735         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1736     }
1738     // quit if run by the attr_changed listener
1739     if (g_object_get_data( tbl, "freeze" )) {
1740         return;
1741     }
1743     // in turn, prevent listener from responding
1744     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1746     bool modmade = false;
1747     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1748     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1749         if (SP_IS_RECT(items->data)) {
1750             if (adj->value != 0) {
1751                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
1752             } else {
1753                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
1754             }
1755             modmade = true;
1756         }
1757     }
1759     sp_rtb_sensitivize( tbl );
1761     if (modmade) {
1762         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
1763                                    _("Change rectangle"));
1764     }
1766     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1769 static void
1770 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
1772     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
1775 static void
1776 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
1778     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
1781 static void
1782 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
1784     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
1787 static void
1788 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
1790     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
1795 static void
1796 sp_rtb_defaults( GtkWidget *widget, GObject *obj)
1798     GtkAdjustment *adj = 0;
1800     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
1801     gtk_adjustment_set_value(adj, 0.0);
1802     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
1803     gtk_adjustment_value_changed(adj);
1805     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
1806     gtk_adjustment_set_value(adj, 0.0);
1807     gtk_adjustment_value_changed(adj);
1809     sp_rtb_sensitivize( obj );
1812 static void rect_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1813                                        gchar const *old_value, gchar const *new_value,
1814                                        bool is_interactive, gpointer data)
1816     GObject *tbl = G_OBJECT(data);
1818     // quit if run by the _changed callbacks
1819     if (g_object_get_data( tbl, "freeze" )) {
1820         return;
1821     }
1823     // in turn, prevent callbacks from responding
1824     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
1826     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1827     SPUnit const *unit = tracker->getActiveUnit();
1829     gpointer item = g_object_get_data( tbl, "item" );
1830     if (item && SP_IS_RECT(item)) {
1831         {
1832             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
1833             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
1834             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
1835         }
1837         {
1838             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
1839             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
1840             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
1841         }
1843         {
1844             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
1845             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
1846             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
1847         }
1849         {
1850             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
1851             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
1852             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
1853         }
1854     }
1856     sp_rtb_sensitivize( tbl );
1858     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1862 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
1863     NULL, /* child_added */
1864     NULL, /* child_removed */
1865     rect_tb_event_attr_changed,
1866     NULL, /* content_changed */
1867     NULL  /* order_changed */
1868 };
1870 /**
1871  *  \param selection should not be NULL.
1872  */
1873 static void
1874 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1876     int n_selected = 0;
1877     Inkscape::XML::Node *repr = NULL;
1878     SPItem *item = NULL;
1879     Inkscape::XML::Node *oldrepr = NULL;
1881     for (GSList const *items = selection->itemList();
1882          items != NULL;
1883          items = items->next) {
1884         if (SP_IS_RECT((SPItem *) items->data)) {
1885             n_selected++;
1886             item = (SPItem *) items->data;
1887             repr = SP_OBJECT_REPR(item);
1888         }
1889     }
1891     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1893     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
1895     if (n_selected == 0) {
1896         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1898         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
1899         gtk_action_set_sensitive(w, FALSE);
1900         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
1901         gtk_action_set_sensitive(h, FALSE);
1903     } else if (n_selected == 1) {
1904         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1905         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
1907         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
1908         gtk_action_set_sensitive(w, TRUE);
1909         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
1910         gtk_action_set_sensitive(h, TRUE);
1912         oldrepr = (Inkscape::XML::Node *) g_object_get_data( tbl, "repr" );
1913         if (oldrepr) { // remove old listener
1914             sp_repr_remove_listener_by_data(oldrepr, tbl);
1915             Inkscape::GC::release(oldrepr);
1916             oldrepr = 0;
1917             g_object_set_data( tbl, "repr", NULL );
1918             g_object_set_data( tbl, "item", NULL );
1919         }
1920         if (repr) {
1921             g_object_set_data( tbl, "repr", repr );
1922             g_object_set_data( tbl, "item", item );
1923             Inkscape::GC::anchor(repr);
1924             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
1925             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
1926         }
1927     } else {
1928         // FIXME: implement averaging of all parameters for multiple selected
1929         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1930         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1931         sp_rtb_sensitivize( tbl );
1932     }
1936 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1938     EgeAdjustmentAction* eact = 0;
1940     {
1941         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
1942         ege_output_action_set_use_markup( act, TRUE );
1943         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1944         g_object_set_data( holder, "mode_action", act );
1945     }
1947     // rx/ry units menu: create
1948     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1949     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
1950     // fixme: add % meaning per cent of the width/height
1951     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1952     g_object_set_data( holder, "tracker", tracker );
1954     /* W */
1955     {
1956         eact = create_adjustment_action( "RectWidthAction",
1957                                          _("W:"), _("Width of rectangle"),
1958                                          "tools.shapes.rect", "width", 0,
1959                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
1960                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1961                                          0, 0, 0,
1962                                          sp_rtb_width_value_changed );
1963         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1964         g_object_set_data( holder, "width_action", eact );
1965         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1966         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1967     }
1969     /* H */
1970     {
1971         eact = create_adjustment_action( "RectHeightAction",
1972                                          _("H:"), _("Height of rectangle"),
1973                                          "tools.shapes.rect", "height", 0,
1974                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1975                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1976                                          0, 0, 0,
1977                                          sp_rtb_height_value_changed );
1978         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1979         g_object_set_data( holder, "height_action", eact );
1980         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1981         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1982     }
1984     /* rx */
1985     {
1986         eact = create_adjustment_action( "RadiusXAction",
1987                                          _("Rx:"), _("Horizontal radius of rounded corners"),
1988                                          "tools.shapes.rect", "rx", 0,
1989                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1990                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1991                                          0, 0, 0,
1992                                          sp_rtb_rx_value_changed);
1993         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1994         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1995     }
1997     /* ry */
1998     {
1999         eact = create_adjustment_action( "RadiusYAction",
2000                                          _("Ry:"), _("Vertical radius of rounded corners"),
2001                                          "tools.shapes.rect", "ry", 0,
2002                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2003                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2004                                          0, 0, 0,
2005                                          sp_rtb_ry_value_changed);
2006         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2007         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2008     }
2010     // add the units menu
2011     {
2012         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), _("") );
2013         gtk_action_group_add_action( mainActions, act );
2014     }
2016     /* Reset */
2017     {
2018         InkAction* inky = ink_action_new( "RectResetAction",
2019                                           _("Not rounded"),
2020                                           _("Make corners sharp"),
2021                                           "squared_corner",
2022                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2023         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2024         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2025         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2026         g_object_set_data( holder, "not_rounded", inky );
2027     }
2029     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2030     sp_rtb_sensitivize( holder );
2032     sigc::connection *connection = new sigc::connection(
2033         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2034         );
2035     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2038 //########################
2039 //##       Spiral       ##
2040 //########################
2042 static void
2043 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2045     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2047     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2048         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2049     }
2051     // quit if run by the attr_changed listener
2052     if (g_object_get_data( tbl, "freeze" )) {
2053         return;
2054     }
2056     // in turn, prevent listener from responding
2057     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2059     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2061     bool modmade = false;
2062     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2063          items != NULL;
2064          items = items->next)
2065     {
2066         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2067             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2068             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2069             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2070             modmade = true;
2071         }
2072     }
2074     g_free(namespaced_name);
2076     if (modmade) {
2077         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2078                                    _("Change spiral"));
2079     }
2081     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2084 static void
2085 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2087     sp_spl_tb_value_changed(adj, tbl, "revolution");
2090 static void
2091 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2093     sp_spl_tb_value_changed(adj, tbl, "expansion");
2096 static void
2097 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2099     sp_spl_tb_value_changed(adj, tbl, "t0");
2102 static void
2103 sp_spl_tb_defaults(GtkWidget *widget, GtkObject *obj)
2105     GtkWidget *tbl = GTK_WIDGET(obj);
2107     GtkAdjustment *adj;
2109     // fixme: make settable
2110     gdouble rev = 5;
2111     gdouble exp = 1.0;
2112     gdouble t0 = 0.0;
2114     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2115     gtk_adjustment_set_value(adj, rev);
2116     gtk_adjustment_value_changed(adj);
2118     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2119     gtk_adjustment_set_value(adj, exp);
2120     gtk_adjustment_value_changed(adj);
2122     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2123     gtk_adjustment_set_value(adj, t0);
2124     gtk_adjustment_value_changed(adj);
2126     spinbutton_defocus(GTK_OBJECT(tbl));
2130 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2131                                          gchar const *old_value, gchar const *new_value,
2132                                          bool is_interactive, gpointer data)
2134     GtkWidget *tbl = GTK_WIDGET(data);
2136     // quit if run by the _changed callbacks
2137     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2138         return;
2139     }
2141     // in turn, prevent callbacks from responding
2142     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2144     GtkAdjustment *adj;
2145     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2146     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2148     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2149     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2151     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2152     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2154     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2158 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2159     NULL, /* child_added */
2160     NULL, /* child_removed */
2161     spiral_tb_event_attr_changed,
2162     NULL, /* content_changed */
2163     NULL  /* order_changed */
2164 };
2166 static void
2167 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2169     int n_selected = 0;
2170     Inkscape::XML::Node *repr = NULL;
2171     Inkscape::XML::Node *oldrepr = NULL;
2173     for (GSList const *items = selection->itemList();
2174          items != NULL;
2175          items = items->next)
2176     {
2177         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2178             n_selected++;
2179             repr = SP_OBJECT_REPR((SPItem *) items->data);
2180         }
2181     }
2183     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2185     if (n_selected == 0) {
2186         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2187     } else if (n_selected == 1) {
2188         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2190         oldrepr = (Inkscape::XML::Node *) g_object_get_data( tbl, "repr" );
2191         if (oldrepr) { // remove old listener
2192             sp_repr_remove_listener_by_data(oldrepr, tbl);
2193             Inkscape::GC::release(oldrepr);
2194             oldrepr = 0;
2195             g_object_set_data( tbl, "repr", NULL );
2196         }
2198         if (repr) {
2199             g_object_set_data( tbl, "repr", repr );
2200             Inkscape::GC::anchor(repr);
2201             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2202             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2203         }
2204     } else {
2205         // FIXME: implement averaging of all parameters for multiple selected
2206         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2207         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2208     }
2212 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2214     EgeAdjustmentAction* eact = 0;
2216     {
2217         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2218         ege_output_action_set_use_markup( act, TRUE );
2219         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2220         g_object_set_data( holder, "mode_action", act );
2221     }
2223     /* Revolution */
2224     {
2225         eact = create_adjustment_action( "SpiralRevolutionAction",
2226                                          _("Turns:"), _("Number of revolutions"),
2227                                          "tools.shapes.spiral", "revolution", 3.0,
2228                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2229                                          0.01, 1024.0, 0.1, 1.0,
2230                                          0, 0, 0,
2231                                          sp_spl_tb_revolution_value_changed, 1, 2);
2232         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2233     }
2235     /* Expansion */
2236     {
2237         eact = create_adjustment_action( "SpiralExpansionAction",
2238                                          _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2239                                          "tools.shapes.spiral", "expansion", 1.0,
2240                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2241                                          0.0, 1000.0, 0.01, 1.0,
2242                                          0, 0, 0,
2243                                          sp_spl_tb_expansion_value_changed);
2244         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2245     }
2247     /* T0 */
2248     {
2249         eact = create_adjustment_action( "SpiralT0Action",
2250                                          _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2251                                          "tools.shapes.spiral", "t0", 0.0,
2252                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2253                                          0.0, 0.999, 0.01, 1.0,
2254                                          0, 0, 0,
2255                                          sp_spl_tb_t0_value_changed);
2256         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2257     }
2259     /* Reset */
2260     {
2261         InkAction* inky = ink_action_new( "SpiralResetAction",
2262                                           _("Defaults"),
2263                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2264                                           GTK_STOCK_CLEAR,
2265                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2266         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2267         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2268     }
2271     sigc::connection *connection = new sigc::connection(
2272         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2273         );
2274     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2277 //########################
2278 //##     Pen/Pencil    ##
2279 //########################
2282 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2284     // Put stuff here
2287 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2289     // Put stuff here
2293 //########################
2294 //##     Calligraphy    ##
2295 //########################
2297 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
2299     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2302 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
2304     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2307 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
2309     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2312 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
2314     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2317 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
2319     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
2322 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
2324     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
2327 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
2329     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
2332 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
2334     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
2337 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer data )
2339     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2342 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer data )
2344     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
2347 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
2349     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
2351     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
2354 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
2356     // FIXME: make defaults settable via Inkscape Options
2357     struct KeyValue {
2358         char const *key;
2359         double value;
2360     } const key_values[] = {
2361         {"mass", 0.02},
2362         {"wiggle", 0.0},
2363         {"angle", 30.0},
2364         {"width", 15},
2365         {"thinning", 0.1},
2366         {"tremor", 0.0},
2367         {"flatness", 0.9},
2368         {"cap_rounding", 0.0}
2369     };
2371     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
2372         KeyValue const &kv = key_values[i];
2373         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
2374         if ( adj ) {
2375             gtk_adjustment_set_value(adj, kv.value);
2376         }
2377     }
2381 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2383     {
2384         EgeAdjustmentAction* calligraphy_angle = 0;
2386         {
2387         /* Width */
2388         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
2389         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2390         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
2391                                          _("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
2392                                          "tools.calligraphic", "width", 15,
2393                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
2394                                          1, 100, 1.0, 10.0,
2395                                          labels, values, G_N_ELEMENTS(labels),
2396                                          sp_ddc_width_value_changed,  0.01, 0, 100 );
2397         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2398         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2399         }
2401         {
2402         /* Thinning */
2403             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
2404             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
2405         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
2406                                          _("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
2407                                          "tools.calligraphic", "thinning", 0.1,
2408                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2409                                          -1.0, 1.0, 0.01, 0.1,
2410                                          labels, values, G_N_ELEMENTS(labels),
2411                                          sp_ddc_velthin_value_changed, 0.01, 2);
2412         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2413         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2414         }
2416         {
2417         /* Angle */
2418         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
2419         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2420         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
2421                                          _("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
2422                                          "tools.calligraphic", "angle", 30,
2423                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
2424                                          -90.0, 90.0, 1.0, 10.0,
2425                                          labels, values, G_N_ELEMENTS(labels),
2426                                          sp_ddc_angle_value_changed, 1, 0 );
2427         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2428         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2429         calligraphy_angle = eact;
2430         }
2432         {
2433         /* Fixation */
2434             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
2435         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
2436         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
2437                                          _("Fixation:"), _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
2438                                          "tools.calligraphic", "flatness", 0.9,
2439                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2440                                          0.0, 1.0, 0.01, 0.1,
2441                                          labels, values, G_N_ELEMENTS(labels),
2442                                          sp_ddc_flatness_value_changed, 0.01, 2 );
2443         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2444         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2445         }
2447         {
2448         /* Cap Rounding */
2449             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
2450         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
2451         // TRANSLATORS: "cap" means "end" (both start and finish) here
2452         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
2453                                          _("Caps:"), _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
2454                                          "tools.calligraphic", "cap_rounding", 0.0,
2455                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2456                                          0.0, 5.0, 0.01, 0.1,
2457                                          labels, values, G_N_ELEMENTS(labels),
2458                                          sp_ddc_cap_rounding_value_changed, 0.01, 2 );
2459         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2460         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2461         }
2463         {
2464         /* Tremor */
2465             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
2466         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
2467         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
2468                                          _("Tremor:"), _("Increase to make strokes rugged and trembling"),
2469                                          "tools.calligraphic", "tremor", 0.0,
2470                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2471                                          0.0, 1.0, 0.01, 0.1,
2472                                          labels, values, G_N_ELEMENTS(labels),
2473                                          sp_ddc_tremor_value_changed, 0.01, 2 );
2475         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2476         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2477         }
2479         {
2480         /* Wiggle */
2481         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
2482         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
2483         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
2484                                          _("Wiggle:"), _("Increase to make the pen waver and wiggle"),
2485                                          "tools.calligraphic", "wiggle", 0.0,
2486                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2487                                          0.0, 1.0, 0.01, 0.1,
2488                                          labels, values, G_N_ELEMENTS(labels),
2489                                          sp_ddc_wiggle_value_changed, 0.01, 2 );
2490         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2491         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2492         }
2494         {
2495         /* Mass */
2496             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
2497         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
2498         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
2499                                          _("Mass:"), _("Increase to make the pen drag behind, as if slowed by inertia"),
2500                                          "tools.calligraphic", "mass", 0.02,
2501                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2502                                          0.0, 1.0, 0.01, 0.1,
2503                                          labels, values, G_N_ELEMENTS(labels),
2504                                          sp_ddc_mass_value_changed, 0.01, 2 );
2505         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2506         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2507         }
2510         /* Trace Background button */
2511         {
2512             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
2513                                                           _("Trace Background"),
2514                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
2515                                                           "trace_background",
2516                                                           Inkscape::ICON_SIZE_DECORATION );
2517             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2518             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
2519             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
2520         }
2522         /* Use Pressure button */
2523         {
2524             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
2525                                                           _("Pressure"),
2526                                                           _("Use the pressure of the input device to alter the width of the pen"),
2527                                                           "use_pressure",
2528                                                           Inkscape::ICON_SIZE_DECORATION );
2529             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2530             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
2531             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
2532         }
2534         /* Use Tilt button */
2535         {
2536             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
2537                                                           _("Tilt"),
2538                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
2539                                                           "use_tilt",
2540                                                           Inkscape::ICON_SIZE_DECORATION );
2541             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2542             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
2543             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2544             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2545         }
2547         /* Reset */
2548         {
2549             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
2550                                              _("Defaults"),
2551                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2552                                              GTK_STOCK_CLEAR );
2553             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
2554             gtk_action_group_add_action( mainActions, act );
2555             gtk_action_set_sensitive( act, TRUE );
2556         }
2557     }
2561 //########################
2562 //##    Circle / Arc    ##
2563 //########################
2565 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
2567     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
2568     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
2570     if (v1 == 0 && v2 == 0) {
2571         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
2572             gtk_action_set_sensitive( ocb, FALSE );
2573             gtk_action_set_sensitive( make_whole, FALSE );
2574         }
2575     } else {
2576         gtk_action_set_sensitive( ocb, TRUE );
2577         gtk_action_set_sensitive( make_whole, TRUE );
2578     }
2581 static void
2582 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
2584     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2586     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2587         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
2588     }
2590     // quit if run by the attr_changed listener
2591     if (g_object_get_data( tbl, "freeze" )) {
2592         return;
2593     }
2595     // in turn, prevent listener from responding
2596     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2598     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2600     bool modmade = false;
2601     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2602          items != NULL;
2603          items = items->next)
2604     {
2605         SPItem *item = SP_ITEM(items->data);
2607         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
2609             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
2610             SPArc *arc = SP_ARC(item);
2612             if (!strcmp(value_name, "start"))
2613                 ge->start = (adj->value * M_PI)/ 180;
2614             else
2615                 ge->end = (adj->value * M_PI)/ 180;
2617             sp_genericellipse_normalize(ge);
2618             ((SPObject *)arc)->updateRepr();
2619             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
2621             modmade = true;
2622         }
2623     }
2625     g_free(namespaced_name);
2627     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
2629     sp_arctb_sensitivize( tbl, adj->value, other->value );
2631     if (modmade) {
2632         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
2633                                    _("Arc: Change start/end"));
2634     }
2636     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2640 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
2642     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
2645 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
2647     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
2650 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
2652     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2653     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2654         if ( ege_select_one_action_get_active( act ) != 0 ) {
2655             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
2656         } else {
2657             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
2658         }
2659     }
2661     // quit if run by the attr_changed listener
2662     if (g_object_get_data( tbl, "freeze" )) {
2663         return;
2664     }
2666     // in turn, prevent listener from responding
2667     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2669     bool modmade = false;
2671     if ( ege_select_one_action_get_active(act) != 0 ) {
2672         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2673              items != NULL;
2674              items = items->next)
2675         {
2676             if (SP_IS_ARC((SPItem *) items->data)) {
2677                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2678                 repr->setAttribute("sodipodi:open", "true");
2679                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2680                 modmade = true;
2681             }
2682         }
2683     } else {
2684         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2685              items != NULL;
2686              items = items->next)
2687         {
2688             if (SP_IS_ARC((SPItem *) items->data))    {
2689                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2690                 repr->setAttribute("sodipodi:open", NULL);
2691                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2692                 modmade = true;
2693             }
2694         }
2695     }
2697     if (modmade) {
2698         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
2699                                    _("Arc: Change open/closed"));
2700     }
2702     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2705 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
2707     GtkAdjustment *adj;
2708     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
2709     gtk_adjustment_set_value(adj, 0.0);
2710     gtk_adjustment_value_changed(adj);
2712     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
2713     gtk_adjustment_set_value(adj, 0.0);
2714     gtk_adjustment_value_changed(adj);
2716     spinbutton_defocus( GTK_OBJECT(obj) );
2719 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2720                                       gchar const *old_value, gchar const *new_value,
2721                                       bool is_interactive, gpointer data)
2723     GObject *tbl = G_OBJECT(data);
2725     // quit if run by the _changed callbacks
2726     if (g_object_get_data( tbl, "freeze" )) {
2727         return;
2728     }
2730     // in turn, prevent callbacks from responding
2731     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2733     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
2734     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
2736     GtkAdjustment *adj1,*adj2;
2737     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
2738     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
2739     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
2740     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
2742     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
2744     char const *openstr = NULL;
2745     openstr = repr->attribute("sodipodi:open");
2746     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
2748     if (openstr) {
2749         ege_select_one_action_set_active( ocb, 1 );
2750     } else {
2751         ege_select_one_action_set_active( ocb, 0 );
2752     }
2754     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2757 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
2758     NULL, /* child_added */
2759     NULL, /* child_removed */
2760     arc_tb_event_attr_changed,
2761     NULL, /* content_changed */
2762     NULL  /* order_changed */
2763 };
2766 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2768     int n_selected = 0;
2769     Inkscape::XML::Node *repr = NULL;
2770     Inkscape::XML::Node *oldrepr = NULL;
2772     for (GSList const *items = selection->itemList();
2773          items != NULL;
2774          items = items->next)
2775     {
2776         if (SP_IS_ARC((SPItem *) items->data)) {
2777             n_selected++;
2778             repr = SP_OBJECT_REPR((SPItem *) items->data);
2779         }
2780     }
2782     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2784     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2785     if (n_selected == 0) {
2786         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2787     } else if (n_selected == 1) {
2788         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2789         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2791         oldrepr = (Inkscape::XML::Node *) g_object_get_data( tbl, "repr" );
2793         if (oldrepr) { // remove old listener
2794             sp_repr_remove_listener_by_data(oldrepr, tbl);
2795             Inkscape::GC::release(oldrepr);
2796             oldrepr = 0;
2797             g_object_set_data( tbl, "repr", NULL );
2798         }
2800         if (repr) {
2801             g_object_set_data( tbl, "repr", repr );
2802             Inkscape::GC::anchor(repr);
2803             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
2804             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
2805         }
2806     } else {
2807         // FIXME: implement averaging of all parameters for multiple selected
2808         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2809         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2810         sp_arctb_sensitivize( tbl, 1, 0 );
2811     }
2815 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2817     EgeAdjustmentAction* eact = 0;
2820     {
2821         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
2822         ege_output_action_set_use_markup( act, TRUE );
2823         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2824         g_object_set_data( holder, "mode_action", act );
2825     }
2827     /* Start */
2828     {
2829         eact = create_adjustment_action( "ArcStartAction",
2830                                          _("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
2831                                          "tools.shapes.arc", "start", 0.0,
2832                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
2833                                          -360.0, 360.0, 1.0, 10.0,
2834                                          0, 0, 0,
2835                                          sp_arctb_start_value_changed);
2836         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2837     }
2839     /* End */
2840     {
2841         eact = create_adjustment_action( "ArcEndAction",
2842                                          _("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
2843                                          "tools.shapes.arc", "end", 0.0,
2844                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2845                                          -360.0, 360.0, 1.0, 10.0,
2846                                          0, 0, 0,
2847                                          sp_arctb_end_value_changed);
2848         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2849     }
2851     /* Segments / Pie checkbox */
2852     {
2853         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2855         GtkTreeIter iter;
2856         gtk_list_store_append( model, &iter );
2857         gtk_list_store_set( model, &iter,
2858                             0, _("Closed arc"),
2859                             1, _("Switch to segment (closed shape with two radii)"),
2860                             2, "circle_closed_arc",
2861                             -1 );
2863         gtk_list_store_append( model, &iter );
2864         gtk_list_store_set( model, &iter,
2865                             0, _("Open Arc"),
2866                             1, _("Switch to arc (unclosed shape)"),
2867                             2, "circle_open_arc",
2868                             -1 );
2870         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", _(""), _(""), NULL, GTK_TREE_MODEL(model) );
2871         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2872         g_object_set_data( holder, "open_action", act );
2874         ege_select_one_action_set_appearance( act, "full" );
2875         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2876         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2877         ege_select_one_action_set_icon_column( act, 2 );
2878         ege_select_one_action_set_tooltip_column( act, 1  );
2880         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
2881         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
2882         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
2883         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
2884     }
2886     /* Make Whole */
2887     {
2888         InkAction* inky = ink_action_new( "ArcResetAction",
2889                                           _("Make whole"),
2890                                           _("Make the shape a whole ellipse, not arc or segment"),
2891                                           "reset_circle",
2892                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2893         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
2894         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2895         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2896         g_object_set_data( holder, "make_whole", inky );
2897     }
2899     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
2900     // sensitivize make whole and open checkbox
2901     {
2902         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
2903         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
2904         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
2905     }
2908     sigc::connection *connection = new sigc::connection(
2909         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
2910         );
2911     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2917 // toggle button callbacks and updaters
2919 //########################
2920 //##      Dropper       ##
2921 //########################
2923 static void toggle_dropper_pick_alpha (GtkWidget *button, gpointer tbl) {
2924     prefs_set_int_attribute ("tools.dropper", "pick",
2925         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
2926     GtkWidget *set_checkbox = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "set_checkbox");
2927     if (set_checkbox) {
2928         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
2929             gtk_widget_set_sensitive (GTK_WIDGET (set_checkbox), TRUE);
2930         } else {
2931             gtk_widget_set_sensitive (GTK_WIDGET (set_checkbox), FALSE);
2932         }
2933     }
2934     spinbutton_defocus(GTK_OBJECT(tbl));
2937 static void toggle_dropper_set_alpha (GtkWidget *button, gpointer tbl) {
2938     prefs_set_int_attribute ("tools.dropper", "setalpha",
2939         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 1 : 0);
2940     spinbutton_defocus(GTK_OBJECT(tbl));
2944 /**
2945  * Copy the current saved desktop color to the clipboard as full hex + alpha
2946  * color representation. This is useful for passing values between various
2947  * input boxes, or directly to xml.
2948  */
2949 /* static void
2950 sp_dropper_copy( GtkWidget *widget, GtkObject *obj)
2952     GtkWidget *tbl = GTK_WIDGET(obj);
2954     SPDesktop *desktop =
2955         (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
2958     sp_dropper_c32_color_copy( sp_desktop_get_color(desktop, true) );
2959 }*/
2962 /**
2963  * Copies currently saved desktop color to the clipboard as a hex value. This
2964  * is useful for editing webpages and needing a value quickly for web
2965  * colors.
2966  *
2967  * TODO: When the toggle of the dropper is set to not mix color against
2968  *       page background, this still just gets the color of the page and
2969  *       doesn't get the actual mixed against background which is needed
2970  *       for the hex value ppl. want for web pages, etc.
2971  */
2973 /* static void
2974 sp_dropper_copy_as_hex ( GtkWidget *widget, GtkObject *obj)
2976     GtkWidget *tbl = GTK_WIDGET(obj);
2978     SPDesktop *desktop =
2979         (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
2981     sp_dropper_c32_color_copy_hex( sp_desktop_get_color(desktop, true) );
2982 }*/
2985 /**
2986  * Sets the input boxes with the changed color and opacity. This is used as a
2987  * callback for style changing.
2988  */
2989 /* static bool
2990 sp_style_changed (const SPCSSAttr *css, gpointer data)
2992     // GrDrag *drag = (GrDrag *) data;
2994     // set fill of text entry box
2995     if (css->attribute("fill"))
2996         gtk_entry_set_text((GtkEntry *)dropper_rgb_entry,
2997             css->attribute("fill"));
2999     // set opacity of text entry box
3000     if (css->attribute("fill-opacity"))
3001         gtk_entry_set_text((GtkEntry *)dropper_opacity_entry,
3002             css->attribute("fill-opacity"));
3004     // set fill of text entry box
3005     if (css->attribute("stroke"))
3006         gtk_entry_set_text((GtkEntry *)dropper_rgb_entry,
3007             css->attribute("stroke"));
3009     // set opacity of text entry box
3010     if (css->attribute("stroke-opacity"))
3011         gtk_entry_set_text((GtkEntry *)dropper_opacity_entry,
3012             css->attribute("stroke-opacity"));
3013     return false;
3016 */
3019 /**
3020  * Dropper auxiliary toolbar construction and setup.
3021  *
3022  * TODO: Would like to add swatch of current color.
3023  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3024  *       can drag and drop places. Will provide a nice mixing palette.
3025  */
3026 static GtkWidget *
3027 sp_dropper_toolbox_new(SPDesktop *desktop)
3029     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
3031     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
3032     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
3034     GtkTooltips *tt = gtk_tooltips_new();
3037     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE,
3038                        AUX_BETWEEN_BUTTON_GROUPS);
3039     // sp_toolbox_add_label(tbl, _("<b>New:</b>"));
3043     /* RGB Input Field */
3044  /*   {
3045         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3046         GtkWidget *dropper_rgba_label = gtk_label_new ("Color:");
3047         gtk_widget_show (dropper_rgba_label);
3048         gtk_container_add(GTK_CONTAINER(hb), dropper_rgba_label);
3050         dropper_rgb_entry = gtk_entry_new ();
3051         sp_dialog_defocus_on_enter (dropper_rgb_entry);
3052         gtk_entry_set_max_length (GTK_ENTRY (dropper_rgb_entry), 7);
3053         gtk_entry_set_width_chars (GTK_ENTRY (dropper_rgb_entry), 7);
3054         gtk_tooltips_set_tip(tt, dropper_rgb_entry,
3055                          _("Hexidecimal representation of last selected "
3056                            "color"),
3057                          NULL);
3058         gtk_widget_show (dropper_rgb_entry);
3059         gtk_container_add(GTK_CONTAINER(hb), dropper_rgb_entry);
3061         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
3062                            AUX_BETWEEN_BUTTON_GROUPS);
3063     } */
3065     /* Opacity Input Field */
3066 /*    {
3067         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3068         GtkWidget *dropper_opacity_label = gtk_label_new ( _("Opacity:") );
3069         gtk_widget_show (dropper_opacity_label);
3070         gtk_container_add(GTK_CONTAINER(hb), dropper_opacity_label);
3072         dropper_opacity_entry = gtk_entry_new ();
3073         sp_dialog_defocus_on_enter (dropper_opacity_entry);
3074         gtk_entry_set_max_length (GTK_ENTRY (dropper_opacity_entry), 11);
3075         gtk_entry_set_width_chars (GTK_ENTRY (dropper_opacity_entry), 11);
3076         gtk_tooltips_set_tip(tt, dropper_opacity_entry,
3077                          _("Opacity of last selected color"),
3078                          NULL);
3079         gtk_widget_show (dropper_opacity_entry);
3080         gtk_container_add(GTK_CONTAINER(hb), dropper_opacity_entry);
3082         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
3083                            AUX_BETWEEN_BUTTON_GROUPS);
3084     } */
3087     /* Copy to Clipboard */
3088 /*    {
3089         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3090         GtkWidget *b = gtk_button_new_with_label(_("Copy as RGBA"));
3091         gtk_tooltips_set_tip(tt, b, _("Copy last saved color as hexidecimal "
3092                                       "RGB + Alpha (RGBA) to "
3093                                       "clipboard"),
3094                              NULL);
3095         gtk_widget_show(b);
3096         gtk_container_add(GTK_CONTAINER(hb), b);
3097         gtk_signal_connect(GTK_OBJECT(b), "clicked",
3098             GTK_SIGNAL_FUNC(sp_dropper_copy), tbl);
3099         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
3100                            AUX_BETWEEN_BUTTON_GROUPS);
3101     } */
3104     /* Copy to Clipboard as HEX */
3105 /*    {
3106         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3107         GtkWidget *b = gtk_button_new_with_label(_("Copy as HEX"));
3108         gtk_tooltips_set_tip(tt, b, _("Copy last saved color as "
3109                                       "hexidecimal RGB without alpha "
3110                                       "to clipboard"), NULL);
3111         gtk_widget_show(b);
3112         gtk_container_add(GTK_CONTAINER(hb), b);
3113         gtk_signal_connect(GTK_OBJECT(b), "clicked",
3114             GTK_SIGNAL_FUNC(sp_dropper_copy_as_hex), tbl);
3115         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
3116                            AUX_BETWEEN_BUTTON_GROUPS);
3117     } */
3119     // aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
3121     {
3122         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3124         GtkWidget *button = gtk_check_button_new_with_label(_("Pick alpha"));
3125         gtk_tooltips_set_tip(tt, button, _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"), NULL);
3126         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
3127                                       prefs_get_int_attribute ("tools.dropper",
3128                                                                "pick", 1));
3129         gtk_widget_show(button);
3130         gtk_container_add (GTK_CONTAINER (hb), button);
3131         gtk_object_set_data(GTK_OBJECT(tbl), "pick_checkbox", button);
3132         g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (toggle_dropper_pick_alpha), tbl);
3133         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
3134                    AUX_BETWEEN_BUTTON_GROUPS);
3135     }
3137     {
3138         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3140         GtkWidget *button = gtk_check_button_new_with_label(_("Set alpha"));
3141         gtk_tooltips_set_tip(tt, button, _("If alpha was picked, assign it to selection as fill or stroke transparency"), NULL);
3142         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
3143                                       prefs_get_int_attribute ("tools.dropper",
3144                                                                "setalpha", 1));
3145         gtk_widget_show(button);
3146         gtk_container_add (GTK_CONTAINER (hb), button);
3147         gtk_object_set_data(GTK_OBJECT(tbl), "set_checkbox", button);
3148         g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (toggle_dropper_set_alpha), tbl);
3150         // make sure it's disabled if we're not picking alpha
3151         GtkWidget *pick_checkbox = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "pick_checkbox");
3152         if (pick_checkbox) {
3153             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pick_checkbox))) {
3154                 gtk_widget_set_sensitive (GTK_WIDGET (button), TRUE);
3155             } else {
3156                 gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
3157             }
3158         }
3160         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
3161                    AUX_BETWEEN_BUTTON_GROUPS);
3162     }
3164     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
3167     // where new gtkmm stuff should go
3169     gtk_widget_show_all(tbl);
3170     sp_set_font_size_smaller (tbl);
3172     /*
3173     sigc::connection *connection = new sigc::connection(
3174         desktop->connectSetStyle(
3175             sigc::bind(sigc::ptr_fun(sp_style_changed),
3176                        desktop)) );
3178     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection),
3179                      connection); */
3181     return tbl;
3185 //########################
3186 //##    Text Toolbox    ##
3187 //########################
3188 /*
3189 static void
3190 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3192     //Call back for letter sizing spinbutton
3195 static void
3196 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3198     //Call back for line height spinbutton
3201 static void
3202 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3204     //Call back for horizontal kerning spinbutton
3207 static void
3208 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3210     //Call back for vertical kerning spinbutton
3213 static void
3214 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3216     //Call back for letter rotation spinbutton
3217 }*/
3219 namespace {
3221 bool visible = false;
3223 void
3224 sp_text_toolbox_selection_changed (Inkscape::Selection *selection, GObject *tbl)
3227     SPStyle *query =
3228         sp_style_new ();
3230     int result_family =
3231         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3233     int result_style =
3234         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3236     int result_numbers =
3237         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3239     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3241     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3242     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3243     {
3244         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3246         if (repr)
3247         {
3248             sp_style_read_from_repr (query, repr);
3249         }
3250         else
3251         {
3252             return;
3253         }
3254     }
3256     if (query->text)
3257     {
3258         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3259             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3260             gtk_entry_set_text (GTK_ENTRY (entry), "");
3262         } else if (query->text->font_family.value) {
3264             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3265             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3267             Gtk::TreePath path;
3268             try {
3269                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3270             } catch (...) {
3271                 return;
3272             }
3274             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3275             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3277             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3279             gtk_tree_selection_select_path (tselection, path.gobj());
3280             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3282             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3283         }
3285         //Size
3286         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3287         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3288         g_object_set_data (tbl, "size-block", gpointer(1));
3289         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3290         g_object_set_data (tbl, "size-block", gpointer(0));
3291         free (str);
3293         //Anchor
3294         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3295         {
3296             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3297             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3298             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3299             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3300         }
3301         else
3302         {
3303             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3304             {
3305                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3306                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3307                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3308                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3309             }
3310             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3311             {
3312                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3313                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3314                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3315                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3316             }
3317             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3318             {
3319                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3320                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3321                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3322                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3323             }
3324         }
3326         //Style
3327         {
3328             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3330             gboolean active = gtk_toggle_button_get_active (button);
3331             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3333             if (active != check)
3334             {
3335                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3336                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3337                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3338             }
3339         }
3341         {
3342             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3344             gboolean active = gtk_toggle_button_get_active (button);
3345             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3347             if (active != check)
3348             {
3349                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3350                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3351                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3352             }
3353         }
3355         //Orientation
3356         //locking both buttons, changing one affect all group (both)
3357         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3358         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3360         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3361         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3363         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3364         {
3365             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3366         }
3367         else
3368         {
3369             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3370         }
3371         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3372         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3373     }
3376 void
3377 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint flags, GObject *tbl)
3379     sp_text_toolbox_selection_changed (selection, tbl);
3382 void
3383 sp_text_toolbox_subselection_changed (gpointer dragger, GObject *tbl)
3385     sp_text_toolbox_selection_changed (NULL, tbl);
3388 void
3389 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3390                                 GObject             *tbl)
3392     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3393     GtkTreeModel *model = 0;
3394     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3395     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3396     GtkTreeIter   iter;
3397     char         *family = 0;
3399     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3400     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3402     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3403         return;
3404     }
3406     gtk_tree_model_get (model, &iter, 0, &family, -1);
3408     if (g_object_get_data (G_OBJECT (selection), "block"))
3409     {
3410         gtk_entry_set_text (GTK_ENTRY (entry), family);
3411         return;
3412     }
3414     gtk_widget_hide (popdown);
3415     visible = false;
3417     gtk_entry_set_text (GTK_ENTRY (entry), family);
3419     SPStyle *query =
3420         sp_style_new ();
3422     int result_numbers =
3423         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3425     SPCSSAttr *css = sp_repr_css_attr_new ();
3426     sp_repr_css_set_property (css, "font-family", family);
3428     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3429     if (result_numbers == QUERY_STYLE_NOTHING)
3430     {
3431         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3432         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3433     }
3434     else
3435     {
3436         sp_desktop_set_style (desktop, css, true, true);
3437     }
3439     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3440                                    _("Text: Change font family"));
3441     sp_repr_css_attr_unref (css);
3442     free (family);
3443     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3445     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3448 void
3449 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3450                                        GObject      *tbl)
3452     const char *family = gtk_entry_get_text (entry);
3454     try {
3455         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3456         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3457         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3458         gtk_tree_selection_select_path (selection, path.gobj());
3459         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3460         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3461     } catch (...) {
3462         if (family && strlen (family))
3463         {
3464             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3465         }
3466     }
3469 void
3470 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3471                                    gpointer          data)
3473     if (g_object_get_data (G_OBJECT (button), "block")) return;
3474     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3475     int prop = GPOINTER_TO_INT(data);
3477     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3478     SPCSSAttr *css = sp_repr_css_attr_new ();
3480     switch (prop)
3481     {
3482         case 0:
3483         {
3484             sp_repr_css_set_property (css, "text-anchor", "start");
3485             sp_repr_css_set_property (css, "text-align", "start");
3486             break;
3487         }
3488         case 1:
3489         {
3490             sp_repr_css_set_property (css, "text-anchor", "middle");
3491             sp_repr_css_set_property (css, "text-align", "center");
3492             break;
3493         }
3495         case 2:
3496         {
3497             sp_repr_css_set_property (css, "text-anchor", "end");
3498             sp_repr_css_set_property (css, "text-align", "end");
3499             break;
3500         }
3502         case 3:
3503         {
3504             sp_repr_css_set_property (css, "text-anchor", "start");
3505             sp_repr_css_set_property (css, "text-align", "justify");
3506             break;
3507         }
3508     }
3510     SPStyle *query =
3511         sp_style_new ();
3512     int result_numbers =
3513         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3515     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3516     if (result_numbers == QUERY_STYLE_NOTHING)
3517     {
3518         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3519     }
3521     sp_desktop_set_style (desktop, css, true, true);
3522     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3523                                    _("Text: Change alignment"));
3524     sp_repr_css_attr_unref (css);
3526     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3529 void
3530 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
3531                                gpointer          data)
3533     if (g_object_get_data (G_OBJECT (button), "block")) return;
3535     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3536     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3537     int          prop       = GPOINTER_TO_INT(data);
3538     bool         active     = gtk_toggle_button_get_active (button);
3541     switch (prop)
3542     {
3543         case 0:
3544         {
3545             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
3546             break;
3547         }
3549         case 1:
3550         {
3551             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
3552             break;
3553         }
3554     }
3556     SPStyle *query =
3557         sp_style_new ();
3558     int result_numbers =
3559         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3561     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3562     if (result_numbers == QUERY_STYLE_NOTHING)
3563     {
3564         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3565     }
3567     sp_desktop_set_style (desktop, css, true, true);
3568     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3569                                    _("Text: Change font style"));
3570     sp_repr_css_attr_unref (css);
3572     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3575 void
3576 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
3577                                      gpointer         data)
3579     if (g_object_get_data (G_OBJECT (button), "block")) {
3580         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3581         return;
3582     }
3584     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3585     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3586     int          prop       = GPOINTER_TO_INT(data);
3588     switch (prop)
3589     {
3590         case 0:
3591         {
3592             sp_repr_css_set_property (css, "writing-mode", "lr");
3593             break;
3594         }
3596         case 1:
3597         {
3598             sp_repr_css_set_property (css, "writing-mode", "tb");
3599             break;
3600         }
3601     }
3603     SPStyle *query =
3604         sp_style_new ();
3605     int result_numbers =
3606         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3608     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3609     if (result_numbers == QUERY_STYLE_NOTHING)
3610     {
3611         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3612     }
3614     sp_desktop_set_style (desktop, css, true, true);
3615     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3616                                    _("Text: Change orientation"));
3617     sp_repr_css_attr_unref (css);
3619     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3622 gboolean
3623 sp_text_toolbox_size_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
3625     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3626     if (!desktop) return FALSE;
3628     switch (get_group0_keyval (event)) {
3629         case GDK_Escape: // defocus
3630             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3631             return TRUE; // I consumed the event
3632             break;
3633         case GDK_Return: // defocus
3634         case GDK_KP_Enter:
3635             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3636             return TRUE; // I consumed the event
3637             break;
3638     }
3639     return FALSE;
3642 gboolean
3643 sp_text_toolbox_family_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3645     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3646     if (!desktop) return FALSE;
3648     switch (get_group0_keyval (event)) {
3649         case GDK_Escape: // defocus
3650             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3651             sp_text_toolbox_selection_changed (NULL, tbl); // update
3652             return TRUE; // I consumed the event
3653             break;
3654     }
3655     return FALSE;
3658 gboolean
3659 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3661     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3662     if (!desktop) return FALSE;
3664     switch (get_group0_keyval (event)) {
3665         case GDK_Escape: // defocus
3666             gtk_widget_hide (w);
3667             visible = false;
3668             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3669             return TRUE; // I consumed the event
3670             break;
3671     }
3672     return FALSE;
3676 void
3677 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
3678                                GObject     *tbl)
3680     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3682     if (g_object_get_data (tbl, "size-block")) return;
3684 #if GTK_CHECK_VERSION(2,6,0)
3685     char *text = gtk_combo_box_get_active_text (cbox);
3686 #else // GTK_CHECK_VERSION(2,6,0)
3687     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
3688     GtkTreeIter iter;
3689     char *text = NULL;
3691     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
3692         gtk_tree_model_get (model, &iter, 0, &text, -1);
3693 #endif // GTK_CHECK_VERSION(2,6,0)
3695     SPCSSAttr *css = sp_repr_css_attr_new ();
3696     sp_repr_css_set_property (css, "font-size", text);
3697     free (text);
3699     SPStyle *query =
3700         sp_style_new ();
3701     int result_numbers =
3702         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3704     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3705     if (result_numbers == QUERY_STYLE_NOTHING)
3706     {
3707         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3708     }
3710     sp_desktop_set_style (desktop, css, true, true);
3711     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
3712                                    _("Text: Change font size"));
3713     sp_repr_css_attr_unref (css);
3716     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
3717         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3720 void
3721 sp_text_toolbox_text_popdown_clicked    (GtkButton          *button,
3722                                          GObject            *tbl)
3724     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3725     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3726     int x, y;
3728     if (!visible)
3729     {
3730         gdk_window_get_origin (widget->window, &x, &y);
3731         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
3732         gtk_widget_show_all (popdown);
3734         gdk_pointer_grab (widget->window, TRUE,
3735                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
3736                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
3737                                         GDK_POINTER_MOTION_MASK),
3738                           NULL, NULL, GDK_CURRENT_TIME);
3740         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
3742         visible = true;
3743     }
3744     else
3745     {
3746         gdk_pointer_ungrab (GDK_CURRENT_TIME);
3747         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3748         gtk_widget_hide (popdown);
3749         visible = false;
3750     }
3753 gboolean
3754 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
3755                                  GdkEventFocus    *event,
3756                                  GObject          *tbl)
3758     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
3759     return FALSE;
3762 gboolean
3763 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
3764                                    GdkEventFocus    *event,
3765                                    GObject          *tbl)
3767     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3769     gtk_widget_hide (popdown);
3770     visible = false;
3771     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3772     return TRUE;
3775 void
3776 cell_data_func  (GtkTreeViewColumn *column,
3777                  GtkCellRenderer   *cell,
3778                  GtkTreeModel      *tree_model,
3779                  GtkTreeIter       *iter,
3780                  gpointer           data)
3782     char        *family,
3783         *family_escaped,
3784         *sample_escaped;
3786     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
3788     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
3790     family_escaped = g_markup_escape_text (family, -1);
3791     sample_escaped = g_markup_escape_text (sample, -1);
3793     std::stringstream markup;
3794     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
3795     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
3797     free (family);
3798     free (family_escaped);
3799     free (sample_escaped);
3802 static void delete_completion(GObject *obj, GtkWidget *entry) {
3803     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
3804     if (completion) {
3805         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
3806         g_object_unref (completion);
3807     }
3810 GtkWidget*
3811 sp_text_toolbox_new (SPDesktop *desktop)
3813     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
3815     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
3816     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
3818     GtkTooltips *tt = gtk_tooltips_new();
3819     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
3821     ////////////Family
3822     //Window
3823     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3824     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
3826     //Entry
3827     GtkWidget           *entry = gtk_entry_new ();
3828     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
3829     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
3830     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
3831     gtk_entry_completion_set_text_column (completion, 0);
3832     gtk_entry_completion_set_minimum_key_length (completion, 1);
3833     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
3834     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
3835     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
3836     aux_toolbox_space (tbl, 1);
3837     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
3838     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
3840     //Button
3841     GtkWidget   *button = gtk_button_new ();
3842     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
3843     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
3845     //Popdown
3846     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
3847     GtkWidget           *treeview = gtk_tree_view_new ();
3849     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
3850     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
3851     gtk_tree_view_column_pack_start (column, cell, FALSE);
3852     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
3853     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
3854     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
3856     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
3857     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
3858 #if GTK_CHECK_VERSION(2,6,0)
3859     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
3860 #endif // GTK_CHECK_VERSION(2,6,0)
3862     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
3864     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
3865     gtk_container_add (GTK_CONTAINER (sw), treeview);
3867     gtk_container_add (GTK_CONTAINER (window), sw);
3868     gtk_widget_set_size_request (window, 300, 450);
3870     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
3871     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
3872     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
3874     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
3876     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
3877     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
3879     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
3880     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
3882     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
3883     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
3884     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
3885     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
3886     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
3888     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
3889     aux_toolbox_space (tbl, 1);
3890     GtkWidget *box = gtk_event_box_new ();
3891     gtk_container_add (GTK_CONTAINER (box), image);
3892     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
3893     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
3894     GtkTooltips *tooltips = gtk_tooltips_new ();
3895     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
3896     gtk_widget_hide (GTK_WIDGET (box));
3897     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
3899     ////////////Size
3900     const char *sizes[] = {
3901         "4", "6", "8", "9", "10", "11", "12", "13", "14",
3902         "16", "18", "20", "22", "24", "28",
3903         "32", "36", "40", "48", "56", "64", "72", "144"
3904     };
3906     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
3907     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
3908     gtk_widget_set_size_request (cbox, 80, -1);
3909     aux_toolbox_space (tbl, 1);
3910     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
3911     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
3912     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
3913     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
3915     //spacer
3916     aux_toolbox_space (tbl, 4);
3917     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
3919     ////////////Text anchor
3920     GtkWidget *group   = gtk_radio_button_new (NULL);
3921     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
3922     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
3924     // left
3925     GtkWidget *rbutton = group;
3926     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3927     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
3928     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3930     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3931     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
3932     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
3933     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
3935     // center
3936     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3937     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3938     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
3939     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3941     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3942     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
3943     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
3944     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
3946     // right
3947     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3948     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3949     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
3950     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3952     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3953     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
3954     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
3955     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
3957     // fill
3958     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3959     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3960     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
3961     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3963     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3964     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
3965     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
3966     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
3968     aux_toolbox_space (tbl, 1);
3969     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
3971     //spacer
3972     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
3974     ////////////Text style
3975     row = gtk_hbox_new (FALSE, 4);
3977     // bold
3978     rbutton = gtk_toggle_button_new ();
3979     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3980     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
3981     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3982     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
3984     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3985     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
3986     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
3988     // italic
3989     rbutton = gtk_toggle_button_new ();
3990     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3991     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
3992     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3993     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
3995     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3996     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
3997     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
3999     aux_toolbox_space (tbl, 1);
4000     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4002     //spacer
4003     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4005     ////////////Text orientation
4006     group   = gtk_radio_button_new (NULL);
4007     row     = gtk_hbox_new (FALSE, 4);
4008     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4010     // horizontal
4011     rbutton = group;
4012     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4013     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4014     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4015     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4017     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4018     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4019     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4021     // vertical
4022     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4023     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4024     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4025     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4026     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4028     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4029     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4030     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4031     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4034     //watch selection
4035     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4037     sigc::connection *c_selection_changed =
4038         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4039                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4040     pool->add_connection ("selection-changed", c_selection_changed);
4042     sigc::connection *c_selection_modified =
4043         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4044                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4045     pool->add_connection ("selection-modified", c_selection_modified);
4047     sigc::connection *c_subselection_changed =
4048         new sigc::connection (desktop->connectToolSubselectionChanged
4049                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4050     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4052     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4055 #if 0
4056     // horizontal
4057     {
4058         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4059         GtkWidget *b = group = gtk_radio_button_new (NULL);
4060         gtk_container_add (GTK_CONTAINER (b), px);
4061         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4062         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4063         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4064         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4065     }
4067     // vertical
4068     {
4069         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4070         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4071         gtk_container_add (GTK_CONTAINER (b), px);
4072         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4073         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4074         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4075         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4076     }
4078     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4080     // letter spacing
4081     {
4082         {
4083             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4084             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4085             gtk_container_add (GTK_CONTAINER (hb), image);
4086             gtk_widget_show(image);
4087             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4088         }
4090         {
4091             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between letters"),
4092                                              "tools.text", "letter_spacing", 0.0,
4093                                              us, tbl, FALSE, NULL,
4094                                              -1000.0, 1000.0, 0.1, 0.1,
4095                                              sp_text_letter_changed, 0.1, 1);
4096             gtk_widget_set_size_request (hb, 45, 6);
4097             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4098         }
4099     }
4101     // line spacing
4102     {
4103         {
4104             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4105             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4106             gtk_container_add (GTK_CONTAINER (hb), image);
4107             gtk_widget_show(image);
4108             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4109         }
4111         {
4112             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between lines"),
4113                                              "tools.text", "line_spacing", 0,
4114                                              us, tbl, FALSE, NULL,
4115                                              -1000.0, 1000.0, 0.1, 0.1,
4116                                              sp_text_line_changed, 0.1, 1);
4117             gtk_widget_set_size_request (hb, 45, 0);
4118             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4119         }
4120     }
4122     {
4123         // horizontal kerning/vertical kerning units menu: create
4124         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4125         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4126         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4128         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4130         // horizontal kerning
4131         {
4132             {
4133                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4134                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4135                 gtk_container_add (GTK_CONTAINER (hb), image);
4136                 gtk_widget_show(image);
4137                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4138             }
4140             {
4141                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Horizontal kerning"),
4142                                                  "tools.text", "horizontal_kerning", 0,
4143                                                  us, tbl, FALSE, NULL,
4144                                                  -100.00, 100.00, 0.01, 0.1,
4145                                                  sp_text_horiz_kern_changed);
4146                 gtk_widget_set_size_request (hb, 45, 0);
4147                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4148             }
4149         }
4151         // vertical kerning
4152         {
4153             {
4154                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4155                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4156                 gtk_container_add (GTK_CONTAINER (hb), image);
4157                 gtk_widget_show(image);
4158                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4159             }
4161             {
4162                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Vertical kerning"),
4163                                                  "tools.text", "vertical_kerning", 0,
4164                                                  us, tbl, FALSE, NULL,
4165                                                  -100.00, 100.00, 0.01, 0.1,
4166                                                  sp_text_vert_kern_changed);
4167                 gtk_widget_set_size_request (hb, 45, 0);
4168                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4169             }
4170         }
4172         // add the units menu
4173         gtk_widget_show(us);
4174         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4175         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4176     }
4178     // letter rotation
4179     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4180     {
4181         {
4182             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4183             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4184             gtk_container_add (GTK_CONTAINER (hb), image);
4185             gtk_widget_show(image);
4186             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4187         }
4188         {
4189             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Letter rotation"),
4190                                              "tools.text", "letter_rotation", 0,
4191                                              us, tbl, FALSE, NULL,
4192                                              -180.0, 180.0, 0.1, 0.1,
4193                                              sp_text_letter_rotation_changed, 0.1, 1);
4194             gtk_widget_set_size_request (hb, 45, 0);
4195             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4196         }
4197         // rotation degree label
4198         {
4199             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4200             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4201         }
4202     }
4204     // Remove Manual Kerns
4205     {
4206         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4207         GtkWidget *button = gtk_button_new ();
4208         gtk_container_add (GTK_CONTAINER (button), px);
4209         gtk_widget_show(button);
4210         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4211         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4212         gtk_widget_set_sensitive(button, TRUE);
4213         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4214     }
4215 #endif
4217     gtk_widget_show_all (tbl);
4218     return tbl;
4220 } // end of sp_text_toolbox_new()
4222 }//<unnamed> namespace
4225 //#########################
4226 //##  Connector Toolbox  ##
4227 //#########################
4229 static void sp_connector_path_set_avoid(void)
4231     cc_selection_set_avoid(true);
4235 static void sp_connector_path_set_ignore(void)
4237     cc_selection_set_avoid(false);
4242 static void connector_spacing_changed(GtkAdjustment *adj, GtkWidget *tbl)
4244     // quit if run by the _changed callbacks
4245     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4246         return;
4247     }
4249     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl),
4250             "desktop");
4251     SPDocument *doc = sp_desktop_document(desktop);
4253     if (!sp_document_get_undo_sensitive(doc))
4254     {
4255         return;
4256     }
4258     // in turn, prevent callbacks from responding
4259     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
4261     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4263     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4264     SP_OBJECT(desktop->namedview)->updateRepr();
4266     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4267     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4268         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4269         NR::Matrix m = NR::identity();
4270         avoid_item_move(&m, item);
4271     }
4273     if (items) {
4274         g_slist_free(items);
4275     }
4277     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4278             _("Change connector spacing"));
4280     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
4282     spinbutton_defocus(GTK_OBJECT(tbl));
4285 static void sp_connector_graph_layout(void)
4287     if (!SP_ACTIVE_DESKTOP) return;
4289     // hack for clones, see comment in align-and-distribute.cpp
4290     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4291     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4293     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4295     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4297     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4300 static void
4301 sp_directed_graph_layout_toggled(GtkWidget *widget, GtkObject *tbl)
4303     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
4304         prefs_set_string_attribute("tools.connector", "directedlayout",
4305                 "true");
4306     } else {
4307         prefs_set_string_attribute("tools.connector", "directedlayout",
4308                 "false");
4309     }
4311 static void
4312 sp_nooverlaps_graph_layout_toggled(GtkWidget *widget, GtkObject *tbl)
4314     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
4315         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4316                 "true");
4317     } else {
4318         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4319                 "false");
4320     }
4324 static void connector_length_changed(GtkAdjustment *adj, GtkWidget *tbl)
4326     prefs_set_double_attribute("tools.connector", "length", adj->value);
4327     spinbutton_defocus(GTK_OBJECT(tbl));
4330 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4331         gchar const *name, gchar const *old_value, gchar const *new_value,
4332         bool is_interactive, gpointer data)
4334     GtkWidget *tbl = GTK_WIDGET(data);
4336     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4337         return;
4338     }
4339     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4340         return;
4341     }
4343     GtkAdjustment *adj = (GtkAdjustment*)
4344             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4345     gdouble spacing = defaultConnSpacing;
4346     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4348     gtk_adjustment_set_value(adj, spacing);
4352 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4353     NULL, /* child_added */
4354     NULL, /* child_removed */
4355     connector_tb_event_attr_changed,
4356     NULL, /* content_changed */
4357     NULL  /* order_changed */
4358 };
4361 static GtkWidget *
4362 sp_connector_toolbox_new(SPDesktop *desktop)
4364     GtkTooltips *tt = gtk_tooltips_new();
4365     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
4367     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4368     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4370     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE,
4371             AUX_BETWEEN_BUTTON_GROUPS);
4373     sp_toolbox_button_new(tbl, Inkscape::ICON_SIZE_SMALL_TOOLBAR,
4374             "connector_avoid", GTK_SIGNAL_FUNC(sp_connector_path_set_avoid),
4375             tt, _("Make connectors avoid selected objects"));
4377     sp_toolbox_button_new(tbl, Inkscape::ICON_SIZE_SMALL_TOOLBAR,
4378             "connector_ignore", GTK_SIGNAL_FUNC(sp_connector_path_set_ignore),
4379             tt, _("Make connectors ignore selected objects"));
4381     //  interval
4382     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE,
4383             AUX_BETWEEN_BUTTON_GROUPS);
4385     // Spacing spinbox
4386     {
4387         GtkWidget *object_spacing = sp_tb_spinbutton(_("Spacing:"),
4388                 _("The amount of space left around objects by auto-routing connectors"),
4389                 "tools.connector", "spacing", 10, NULL, tbl, TRUE,
4390                 "inkscape:connector-spacing", 0, 100, 1.0, 10.0,
4391                 connector_spacing_changed, 1, 0);
4393         gtk_box_pack_start(GTK_BOX(tbl), object_spacing, FALSE, FALSE,
4394                 AUX_SPACING);
4395     }
4396     // Graph (connector network) layout
4397     sp_toolbox_button_new(tbl, Inkscape::ICON_SIZE_SMALL_TOOLBAR,
4398             "graph_layout", GTK_SIGNAL_FUNC(sp_connector_graph_layout),
4399             tt, _("Nicely arrange selected connector network"));
4400     // Default connector length spinbox
4401     {
4402         GtkWidget *connector_length = sp_tb_spinbutton(_("Length:"),
4403                 _("Ideal length for connectors when layout is applied"),
4404                 "tools.connector", "length", 100, NULL, tbl, TRUE,
4405                 "inkscape:connector-length", 10, 1000, 10.0, 100.0,
4406                 connector_length_changed, 1, 0);
4408         gtk_box_pack_start(GTK_BOX(tbl), connector_length, FALSE, FALSE,
4409                 AUX_SPACING);
4410     }
4411     gchar const *tbuttonstate;
4412     // Directed edges toggle button
4413     {
4414         GtkWidget *tbutton = gtk_toggle_button_new ();
4415         gtk_button_set_relief       (GTK_BUTTON (tbutton), GTK_RELIEF_NONE);
4416         gtk_container_add           (GTK_CONTAINER (tbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, "directed_graph"));
4417         gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (tbutton), FALSE);
4418         gtk_tooltips_set_tip(tt, tbutton, _("Make connectors with end-markers (arrows) point downwards"), NULL);
4420         gtk_box_pack_start  (GTK_BOX  (tbl), tbutton, FALSE, FALSE, 0);
4421         g_signal_connect(G_OBJECT(tbutton), "toggled", GTK_SIGNAL_FUNC(sp_directed_graph_layout_toggled), tbl);
4422         tbuttonstate = prefs_get_string_attribute("tools.connector", "directedlayout");
4423         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(tbutton),
4424                 (tbuttonstate && !strcmp(tbuttonstate, "true"))?TRUE:FALSE );
4425     }
4426     // Avoid overlaps toggle button
4427     {
4428         GtkWidget *tbutton = gtk_toggle_button_new ();
4429         gtk_button_set_relief       (GTK_BUTTON (tbutton), GTK_RELIEF_NONE);
4430         gtk_container_add           (GTK_CONTAINER (tbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, "remove_overlaps"));
4431         gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (tbutton), FALSE);
4432         gtk_tooltips_set_tip(tt, tbutton, _("Do not allow overlapping shapes"), NULL);
4434         gtk_box_pack_start  (GTK_BOX  (tbl), tbutton, FALSE, FALSE, 0);
4435         g_signal_connect(G_OBJECT(tbutton), "toggled", GTK_SIGNAL_FUNC(sp_nooverlaps_graph_layout_toggled), tbl);
4436         tbuttonstate = prefs_get_string_attribute("tools.connector", "avoidoverlaplayout");
4437         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(tbutton),
4438                 (tbuttonstate && !strcmp(tbuttonstate, "true"))?TRUE:FALSE );
4439     }
4441     gtk_widget_show_all(tbl);
4442     sp_set_font_size_smaller (tbl);
4444     // Code to watch for changes to the connector-spacing attribute in
4445     // the XML.
4446     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4447     g_assert(repr != NULL);
4449     Inkscape::XML::Node *oldrepr = (Inkscape::XML::Node *)
4450             gtk_object_get_data(GTK_OBJECT(tbl), "repr");
4452     if (oldrepr) { // remove old listener
4453         sp_repr_remove_listener_by_data(oldrepr, tbl);
4454         Inkscape::GC::release(oldrepr);
4455         oldrepr = NULL;
4456         g_object_set_data(G_OBJECT(tbl), "repr", NULL);
4457     }
4459     if (repr) {
4460         g_object_set_data(G_OBJECT(tbl), "repr", repr);
4461         Inkscape::GC::anchor(repr);
4462         sp_repr_add_listener(repr, &connector_tb_repr_events, tbl);
4463         sp_repr_synthesize_events(repr, &connector_tb_repr_events, tbl);
4464     }
4466     return tbl;
4468 } // end of sp_connector_toolbox_new()
4470 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* tbl)
4472     gint channels = ege_select_one_action_get_active( act );
4473     flood_channels_set_channels( channels );
4476 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject *tbl)
4478     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4481 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
4483     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
4484     SPUnit const *unit = tracker->getActiveUnit();
4487     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4490 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4492     EgeAdjustmentAction* eact = 0;
4494     {
4495         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4497         GList* items = 0;
4498         gint count = 0;
4499         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
4500         {
4501             GtkTreeIter iter;
4502             gtk_list_store_append( model, &iter );
4503             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4504             count++;
4505         }
4506         g_list_free( items );
4507         items = 0;
4508         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by:"), _(""), NULL, GTK_TREE_MODEL(model) );
4509         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
4510         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
4511         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4512     }
4514     // Spacing spinbox
4515     {
4516         eact = create_adjustment_action(
4517             "ThresholdAction",
4518             _("Threshold:"),
4519             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
4520             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
4521             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
4522             0, 0, 0,
4523             paintbucket_threshold_changed, 1, 0 );
4525         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4526     }
4528     // Create the units menu.
4529     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
4530     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
4531     g_object_set_data( holder, "tracker", tracker );
4532     {
4533         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), _("") );
4534         gtk_action_group_add_action( mainActions, act );
4535     }
4537     // Offset spinbox
4538     {
4539         eact = create_adjustment_action(
4540             "OffsetAction",
4541             _("Grow/shrink by:"),
4542             _("The amount to grow (positive) or shrink (negative) the created fill path"),
4543             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
4544             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
4545             0, 0, 0,
4546             paintbucket_offset_changed, 1, 2);
4547         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4549         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4550     }
4553 /*
4554   Local Variables:
4555   mode:c++
4556   c-file-style:"stroustrup"
4557   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4558   indent-tabs-mode:nil
4559   fill-column:99
4560   End:
4561 */
4562 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :