Code

d8903b404a8a069336d96055c110df74c192d5f6
[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"
91 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
92 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
94 static GtkWidget *sp_node_toolbox_new(SPDesktop *desktop);
95 static GtkWidget *sp_zoom_toolbox_new(SPDesktop *desktop);
96 static GtkWidget *sp_star_toolbox_new(SPDesktop *desktop);
97 static GtkWidget *sp_arc_toolbox_new(SPDesktop *desktop);
98 static GtkWidget *sp_rect_toolbox_new(SPDesktop *desktop);
99 static GtkWidget *sp_spiral_toolbox_new(SPDesktop *desktop);
100 static GtkWidget *sp_pencil_toolbox_new(SPDesktop *desktop);
101 static GtkWidget *sp_pen_toolbox_new(SPDesktop *desktop);
102 static GtkWidget *sp_calligraphy_toolbox_new(SPDesktop *desktop);
103 static GtkWidget *sp_dropper_toolbox_new(SPDesktop *desktop);
104 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
105 static GtkWidget *sp_connector_toolbox_new(SPDesktop *desktop);
106 static GtkWidget *sp_paintbucket_toolbox_new(SPDesktop *desktop);
108 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
111 static struct {
112     gchar const *type_name;
113     gchar const *data_name;
114     sp_verb_t verb;
115     sp_verb_t doubleclick_verb;
116 } const tools[] = {
117     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
118     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
119     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
120     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
121     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
122     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
123     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
124     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
125     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
126     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
127     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
128     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
129     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
130     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
131     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
132     { NULL, NULL, 0, 0 }
133 };
135 static struct {
136     gchar const *type_name;
137     gchar const *data_name;
138     GtkWidget *(*create_func)(SPDesktop *desktop);
139 } const aux_toolboxes[] = {
140     { "SPSelectContext", "select_toolbox", sp_select_toolbox_new },
141     { "SPNodeContext",   "node_toolbox",   sp_node_toolbox_new },
142     { "SPZoomContext",   "zoom_toolbox",   sp_zoom_toolbox_new },
143     { "SPStarContext",   "star_toolbox",   sp_star_toolbox_new },
144     { "SPRectContext",   "rect_toolbox",   sp_rect_toolbox_new },
145     { "SPArcContext",    "arc_toolbox",    sp_arc_toolbox_new },
146     { "SPSpiralContext", "spiral_toolbox", sp_spiral_toolbox_new },
147     { "SPPencilContext", "pencil_toolbox", sp_pencil_toolbox_new },
148     { "SPPenContext", "pen_toolbox", sp_pen_toolbox_new },
149     { "SPDynaDrawContext", "calligraphy_toolbox", sp_calligraphy_toolbox_new },
150     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new },
151     { "SPDropperContext", "dropper_toolbox", sp_dropper_toolbox_new },
152     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new },
153     { "SPConnectorContext", "connector_toolbox", sp_connector_toolbox_new },
154     { "SPFloodContext",  "paintbucket_toolbox",  sp_paintbucket_toolbox_new },
155     { NULL, NULL, NULL }
156 };
158 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
160 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
161 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
163 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
164 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
166 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
167 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
169 /* Global text entry widgets necessary for update */
170 /* GtkWidget *dropper_rgb_entry,
171           *dropper_opacity_entry ; */
172 // should be made a private member once this is converted to class
174 static void delete_connection(GObject *obj, sigc::connection *connection) {
175     connection->disconnect();
176     delete connection;
179 static GtkWidget *
180 sp_toolbox_button_new(GtkWidget *t, Inkscape::IconSize size, gchar const *pxname, GtkSignalFunc handler,
181                       GtkTooltips *tt, gchar const *tip)
183     GtkWidget *b = sp_button_new_from_data(size, SP_BUTTON_TYPE_NORMAL, NULL, pxname, tip, tt);
184     gtk_widget_show(b);
185     if (handler) gtk_signal_connect(GTK_OBJECT(b), "clicked", handler, NULL);
186     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
188     return b;
191 GtkWidget *
192 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
193                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
194                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
196     SPAction *action = verb->get_action(view);
197     if (!action) return NULL;
199     SPAction *doubleclick_action;
200     if (doubleclick_verb)
201         doubleclick_action = doubleclick_verb->get_action(view);
202     else
203         doubleclick_action = NULL;
205     /* fixme: Handle sensitive/unsensitive */
206     /* fixme: Implement sp_button_new_from_action */
207     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
208     gtk_widget_show(b);
209     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
211     return b;
214 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
215                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
217     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
220 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
221                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
223     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
226 GtkWidget *
227 sp_tool_toolbox_new()
229     GtkTooltips *tt = gtk_tooltips_new();
230     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
232     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
233     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
235     gtk_widget_set_sensitive(tb, FALSE);
237     GtkWidget *hb = gtk_handle_box_new();
238     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
239     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
240     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
242     gtk_container_add(GTK_CONTAINER(hb), tb);
243     gtk_widget_show(GTK_WIDGET(tb));
245     sigc::connection* conn = new sigc::connection;
246     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
248     return hb;
251 static void
252 aux_toolbox_attached(GtkHandleBox *toolbox, GtkWidget *child)
254     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
255     gtk_widget_queue_resize(child);
258 static void
259 aux_toolbox_detached(GtkHandleBox *toolbox, GtkWidget *child)
261     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
262     gtk_widget_queue_resize(child);
265 GtkWidget *
266 sp_aux_toolbox_new()
268     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
270     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
271     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
272     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
273     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
274     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
276     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
277     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
279     gtk_widget_set_sensitive(tb, FALSE);
281     GtkWidget *hb = gtk_handle_box_new();
282     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
283     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
284     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
286     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
287     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
289     gtk_container_add(GTK_CONTAINER(hb), tb);
290     gtk_widget_show(GTK_WIDGET(tb));
292     sigc::connection* conn = new sigc::connection;
293     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
295     return hb;
298 //####################################
299 //# Commands Bar
300 //####################################
302 GtkWidget *
303 sp_commands_toolbox_new()
305     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
307     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
308     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
309     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
310     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
311     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
313     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
314     gtk_widget_set_sensitive(tb, FALSE);
316     GtkWidget *hb = gtk_handle_box_new();
317     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
318     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
319     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
321     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
322     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
324     gtk_container_add(GTK_CONTAINER(hb), tb);
325     gtk_widget_show(GTK_WIDGET(tb));
327     sigc::connection* conn = new sigc::connection;
328     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
330     return hb;
334 //####################################
335 //# node editing callbacks
336 //####################################
338 void
339 sp_node_path_edit_add(void)
341     sp_node_selected_add_node();
344 void
345 sp_node_path_edit_delete(void)
347     sp_node_selected_delete();
350 void
351 sp_node_path_edit_delete_segment(void)
353     sp_node_selected_delete_segment();
356 void
357 sp_node_path_edit_break(void)
359     sp_node_selected_break();
362 void
363 sp_node_path_edit_join(void)
365     sp_node_selected_join();
368 void
369 sp_node_path_edit_join_segment(void)
371     sp_node_selected_join_segment();
374 void
375 sp_node_path_edit_toline(void)
377     sp_node_selected_set_line_type(NR_LINETO);
380 void
381 sp_node_path_edit_tocurve(void)
383     sp_node_selected_set_line_type(NR_CURVETO);
386 void
387 sp_node_path_edit_cusp(void)
389     sp_node_selected_set_type(Inkscape::NodePath::NODE_CUSP);
392 void
393 sp_node_path_edit_smooth(void)
395     sp_node_selected_set_type(Inkscape::NodePath::NODE_SMOOTH);
398 void
399 sp_node_path_edit_symmetrical(void)
401     sp_node_selected_set_type(Inkscape::NodePath::NODE_SYMM);
404 static void toggle_show_handles (GtkWidget *button, gpointer data) {
405     bool show = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
406     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
407     sp_nodepath_show_handles(show);
410 //################################
411 //##    Node Editing Toolbox    ##
412 //################################
414 static GtkWidget *
415 sp_node_toolbox_new(SPDesktop *desktop)
417     Inkscape::UI::View::View *view = desktop;
419     GtkTooltips *tt = gtk_tooltips_new();
420     GtkWidget *tb = gtk_hbox_new(FALSE, 0);
422     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
424     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_insert",
425         GTK_SIGNAL_FUNC(sp_node_path_edit_add), tt, _("Insert new nodes into selected segments"));
426     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_delete",
427         GTK_SIGNAL_FUNC(sp_node_path_edit_delete), tt, _("Delete selected nodes"));
429     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
431     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_join",
432         GTK_SIGNAL_FUNC(sp_node_path_edit_join), tt, _("Join selected endnodes"));
433     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_join_segment",
434         GTK_SIGNAL_FUNC(sp_node_path_edit_join_segment), tt, _("Join selected endnodes with a new segment"));
436     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_delete_segment",
437         GTK_SIGNAL_FUNC(sp_node_path_edit_delete_segment), tt, _("Split path between two non-endpoint nodes"));
439     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_break",
440         GTK_SIGNAL_FUNC(sp_node_path_edit_break), tt, _("Break path at selected nodes"));
442     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
444     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_cusp",
445         GTK_SIGNAL_FUNC(sp_node_path_edit_cusp), tt, _("Make selected nodes corner"));
447     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_smooth",
448         GTK_SIGNAL_FUNC(sp_node_path_edit_smooth), tt, _("Make selected nodes smooth"));
450     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_symmetric",
451         GTK_SIGNAL_FUNC(sp_node_path_edit_symmetrical), tt, _("Make selected nodes symmetric"));
453     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
455     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_line",
456         GTK_SIGNAL_FUNC(sp_node_path_edit_toline), tt, _("Make selected segments lines"));
458     sp_toolbox_button_new(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, "node_curve",
459         GTK_SIGNAL_FUNC(sp_node_path_edit_tocurve), tt, _("Make selected segments curves"));
461     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
463     sp_toolbox_button_normal_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_OBJECT_TO_CURVE), view, tt);
465     sp_toolbox_button_normal_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, Inkscape::Verb::get(SP_VERB_SELECTION_OUTLINE), view, tt);
467     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
469     GtkWidget *cvbox = gtk_vbox_new (FALSE, 0);
470     GtkWidget *cbox = gtk_hbox_new (FALSE, 0);
472     {
473     GtkWidget *button = sp_button_new_from_data( Inkscape::ICON_SIZE_DECORATION,
474                                               SP_BUTTON_TYPE_TOGGLE,
475                                               NULL,
476                                               "nodes_show_handles",
477                                               _("Show the Bezier handles of selected nodes"),
478                                               tt);
479     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), prefs_get_int_attribute ("tools.nodes", "show_handles", 1));
480     g_signal_connect_after (G_OBJECT (button), "clicked", G_CALLBACK (toggle_show_handles), desktop);
481     gtk_box_pack_start(GTK_BOX(cbox), button, FALSE, FALSE, 0);
482     }
484     gtk_box_pack_start(GTK_BOX(cvbox), cbox, TRUE, FALSE, 0);
485     gtk_box_pack_start(GTK_BOX(tb), cvbox, FALSE, FALSE, 0);
487     gtk_widget_show_all(tb);
489     return tb;
491 } // end of sp_node_toolbox_new()
494 //########################
495 //##    Zoom Toolbox    ##
496 //########################
498 static GtkWidget *
499 sp_zoom_toolbox_new(SPDesktop *desktop)
501     Inkscape::UI::View::View *view=desktop;
503     GtkTooltips *tt = gtk_tooltips_new();
504     GtkWidget *tb = gtk_hbox_new(FALSE, 0);
506     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0),
507                        FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
509     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_IN), view, tt);
511     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_OUT), view, tt);
513     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
515     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_SELECTION), view, tt);
517     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_DRAWING), view, tt);
519     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_PAGE), view, tt);
521     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_PAGE_WIDTH), view, tt);
523     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
525     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_PREV), view, tt);
527     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_NEXT), view, tt);
529     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
531     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_1_1), view, tt);
533     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_1_2), view, tt);
535     sp_toolbox_button_new_from_verb(tb, Inkscape::ICON_SIZE_SMALL_TOOLBAR, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_2_1), view, tt);
537     gtk_widget_show_all(tb);
539     return tb;
541 } // end of sp_zoom_toolbox_new()
543 void
544 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
546     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")));
550 void
551 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
553     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")));
556 void
557 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
559     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")));
562 static void
563 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
565     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
566     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
568     if (old_desktop) {
569         GList *children, *iter;
571         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
572         for ( iter = children ; iter ; iter = iter->next ) {
573             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
574         }
575         g_list_free(children);
576     }
578     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
580     if (desktop) {
581         gtk_widget_set_sensitive(toolbox, TRUE);
582         setup_func(toolbox, desktop);
583         update_func(desktop, desktop->event_context, toolbox);
584         *conn = desktop->connectEventContextChanged
585             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
586     } else {
587         gtk_widget_set_sensitive(toolbox, FALSE);
588     }
590 } // end of toolbox_set_desktop()
593 static void
594 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
596     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
597     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
598     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
600     for (int i = 0 ; tools[i].type_name ; i++ ) {
601         GtkWidget *button =
602             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
603                                                               SP_BUTTON_TYPE_TOGGLE,
604                                                               Inkscape::Verb::get(tools[i].verb),
605                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
606                                                               desktop,
607                                                               tooltips );
609         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
610                            (gpointer)button );
611     }
615 static void
616 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
618     gchar const *const tname = ( eventcontext
619                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
620                                  : NULL );
621     for (int i = 0 ; tools[i].type_name ; i++ ) {
622         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
623         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
624     }
627 static void
628 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
630     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
632     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
633         GtkWidget *sub_toolbox;
634         if (aux_toolboxes[i].create_func == NULL)
635             sub_toolbox = sp_empty_toolbox_new(desktop);
636         else
637             sub_toolbox = aux_toolboxes[i].create_func(desktop);
639         gtk_size_group_add_widget( grouper, sub_toolbox );
641         gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
642         g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
643     }
644     g_object_unref( G_OBJECT(grouper) );
647 static void
648 update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
650     gchar const *tname = ( eventcontext
651                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
652                            : NULL );
653     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
654         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
655         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
656             gtk_widget_show_all(sub_toolbox);
657             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
658         } else {
659             gtk_widget_hide(sub_toolbox);
660         }
661     }
664 static void
665 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
667     Inkscape::UI::View::View *view = desktop;
669     GtkTooltips *tt = gtk_tooltips_new();
670     GtkWidget *tb = gtk_hbox_new(FALSE, 0);
672     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
673     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
675     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_NEW), view, tt);
676     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_OPEN), view, tt);
677     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_SAVE), view, tt);
678     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_PRINT), view, tt);
680     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
682     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_IMPORT), view, tt);
683     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_FILE_EXPORT), view, tt);
685     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
687     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view, tt);
688     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_REDO), view, tt);
690     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
692     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_COPY), view, tt);
693     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_CUT), view, tt);
694     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view, tt);
696     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
698     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_SELECTION), view, tt);
699     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_DRAWING), view, tt);
700     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_ZOOM_PAGE), view, tt);
702     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
704     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view, tt);
705     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_CLONE), view, tt);
706     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_UNLINK_CLONE), view, tt);
708     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
710     sp_toolbox_button_normal_new_from_verb(tb, toolboxSize, Inkscape::Verb::get(SP_VERB_SELECTION_GROUP), view, tt);
711     sp_toolbox_button_normal_new_from_verb(tb, toolboxSize, Inkscape::Verb::get(SP_VERB_SELECTION_UNGROUP), view, tt);
713     // disabled until we have icons for them:
715     //find
717     //sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_TILE), view, tt);
718     //sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_EDIT_UNTILE), view, tt);
720     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
722     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_FILL_STROKE), view, tt);
723     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), view, tt);
724     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_XML_EDITOR), view, tt);
725     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_ALIGN_DISTRIBUTE), view, tt);
727     aux_toolbox_space(tb, AUX_BETWEEN_BUTTON_GROUPS);
729     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_DISPLAY), view, tt);
730     sp_toolbox_button_new_from_verb(tb, toolboxSize, SP_BUTTON_TYPE_NORMAL, Inkscape::Verb::get(SP_VERB_DIALOG_NAMEDVIEW), view, tt);
732     gtk_widget_show_all(tb);
734     gtk_container_add(GTK_CONTAINER(toolbox), tb);
737 static void
738 update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox)
742 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
744     gtk_widget_show(toolbox_toplevel);
745     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
747     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
748     if (!shown_toolbox) {
749         return;
750     }
751     gtk_widget_show(toolbox);
753     // need to show the spacer, or the padding will be off
754     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
755     gtk_widget_show(spacer);
757     gtk_widget_show_all(shown_toolbox);
760 void
761 aux_toolbox_space(GtkWidget *tb, gint space)
763     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
766 static GtkWidget *
767 sp_empty_toolbox_new(SPDesktop *desktop)
769     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
770     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
771     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
773     gtk_widget_show_all(tbl);
774     sp_set_font_size_smaller (tbl);
776     return tbl;
779 // helper UI functions
781 GtkWidget *
782 sp_tb_spinbutton(
783     gchar *label, gchar const *tooltip,
784     gchar const *path, gchar const *data, gdouble def,
785     GtkWidget *us,
786     GtkWidget *tbl,
787     gboolean altx, gchar const *altx_mark,
788     gdouble lower, gdouble upper, gdouble step, gdouble page,
789     void (*callback)(GtkAdjustment *, GtkWidget *),
790     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
792     GtkTooltips *tt = gtk_tooltips_new();
794     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
796     GtkWidget *l = gtk_label_new(label);
797     gtk_widget_show(l);
798     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
799     gtk_container_add(GTK_CONTAINER(hb), l);
801     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
802                                       lower, upper, step, page, page);
803     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
804     if (us)
805         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
807     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
808     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
809     if (altx)
810         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
811     gtk_widget_set_size_request(sb,
812                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
813                                 AUX_SPINBUTTON_HEIGHT);
814     gtk_widget_show(sb);
815     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
816     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
817     gtk_container_add(GTK_CONTAINER(hb), sb);
818     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
820     return hb;
823 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
824                                                        gchar const *label, gchar const *tooltip,
825                                                        gchar const *path, gchar const *data, gdouble def,
826                                                        GtkWidget *focusTarget,
827                                                        GtkWidget *us,
828                                                        GtkWidget *dataKludge,
829                                                        gboolean altx, gchar const *altx_mark,
830                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
831                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
832                                                        void (*callback)(GtkAdjustment *, GtkWidget *),
833                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
835     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
836                                                              lower, upper, step, page, page ) );
837     if (us) {
838         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
839     }
841     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
843     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
845     if ( (descrCount > 0) && descrLabels && descrValues ) {
846         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
847     }
849     if ( focusTarget ) {
850         ege_adjustment_action_set_focuswidget( act, focusTarget );
851     }
853     if ( altx && altx_mark ) {
854         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
855     }
857     if ( dataKludge ) {
858         gtk_object_set_data( GTK_OBJECT(dataKludge), data, adj );
859     }
861     // Using a cast just to make sure we pass in the right kind of function pointer
862     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
864     return act;
868 #define MODE_LABEL_WIDTH 70
870 //########################
871 //##       Star         ##
872 //########################
874 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GtkWidget *dataKludge )
876     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(dataKludge), "desktop");
878     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
879         // do not remember prefs if this call is initiated by an undo change, because undoing object
880         // creation sets bogus values to its attributes before it is deleted
881         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
882     }
884     // quit if run by the attr_changed listener
885     if (g_object_get_data(G_OBJECT(dataKludge), "freeze")) {
886         return;
887     }
889     // in turn, prevent listener from responding
890     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(TRUE));
892     bool modmade = false;
894     Inkscape::Selection *selection = sp_desktop_selection(desktop);
895     GSList const *items = selection->itemList();
896     for (; items != NULL; items = items->next) {
897         if (SP_IS_STAR((SPItem *) items->data)) {
898             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
899             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
900             sp_repr_set_svg_double(repr, "sodipodi:arg2",
901                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
902                                     + M_PI / (gint)adj->value));
903             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
904             modmade = true;
905         }
906     }
907     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
908                                    _("Star: Change number of corners"));
910     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
913 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GtkWidget *dataKludge )
915     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(dataKludge), "desktop");
917     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
918         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
919     }
921     // quit if run by the attr_changed listener
922     if (g_object_get_data(G_OBJECT(dataKludge), "freeze")) {
923         return;
924     }
926     // in turn, prevent listener from responding
927     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(TRUE));
929     bool modmade = false;
930     Inkscape::Selection *selection = sp_desktop_selection(desktop);
931     GSList const *items = selection->itemList();
932     for (; items != NULL; items = items->next) {
933         if (SP_IS_STAR((SPItem *) items->data)) {
934             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
936             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
937             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
938             if (r2 < r1) {
939                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
940             } else {
941                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
942             }
944             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
945             modmade = true;
946         }
947     }
949     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
950                                    _("Star: Change spoke ratio"));
952     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
955 static void sp_stb_sides_flat_state_changed( GtkToggleAction *act, GtkObject *dataKludge )
957     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(dataKludge), "desktop");
958     bool flat = gtk_toggle_action_get_active(act);
960     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
961         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
962                                     flat ? "true" : "false" );
963     }
965     // quit if run by the attr_changed listener
966     if (g_object_get_data(G_OBJECT(dataKludge), "freeze")) {
967         return;
968     }
970     // in turn, prevent listener from responding
971     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(TRUE));
973     Inkscape::Selection *selection = sp_desktop_selection(desktop);
974     GSList const *items = selection->itemList();
975     GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(dataKludge), "prop_action") );
976     bool modmade = false;
978     if ( prop_action ) {
979         gtk_action_set_sensitive( prop_action, !flat );
980     }
982     for (; items != NULL; items = items->next) {
983         if (SP_IS_STAR((SPItem *) items->data)) {
984             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
985             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
986             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
987             modmade = true;
988         }
989     }
991     if (modmade) {
992         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
993                          flat ? _("Make polygon") : _("Make star"));
994     }
996     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
999 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GtkWidget *dataKludge )
1001     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(dataKludge), "desktop");
1003     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1004         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1005     }
1007     // quit if run by the attr_changed listener
1008     if (g_object_get_data(G_OBJECT(dataKludge), "freeze")) {
1009         return;
1010     }
1012     // in turn, prevent listener from responding
1013     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(TRUE));
1015     bool modmade = false;
1017     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1018     GSList const *items = selection->itemList();
1019     for (; items != NULL; items = items->next) {
1020         if (SP_IS_STAR((SPItem *) items->data)) {
1021             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1022             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1023             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1024             modmade = true;
1025         }
1026     }
1027     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1028                                    _("Star: Change rounding"));
1030     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
1033 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GtkWidget *dataKludge )
1035     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(dataKludge), "desktop");
1037     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1038         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1039     }
1041     // quit if run by the attr_changed listener
1042     if (g_object_get_data(G_OBJECT(dataKludge), "freeze")) {
1043         return;
1044     }
1046     // in turn, prevent listener from responding
1047     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(TRUE));
1049     bool modmade = false;
1051     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1052     GSList const *items = selection->itemList();
1053     for (; items != NULL; items = items->next) {
1054         if (SP_IS_STAR((SPItem *) items->data)) {
1055             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1056             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1057             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1058             modmade = true;
1059         }
1060     }
1061     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1062                                    _("Star: Change randomization"));
1064     g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
1068 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1069                                        gchar const *old_value, gchar const *new_value,
1070                                        bool is_interactive, gpointer data)
1072     GtkWidget *tbl = GTK_WIDGET(data);
1074     // quit if run by the _changed callbacks
1075     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1076         return;
1077     }
1079     // in turn, prevent callbacks from responding
1080     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1082     GtkAdjustment *adj = 0;
1084     if (!strcmp(name, "inkscape:randomized")) {
1085         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1086         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1087     } else if (!strcmp(name, "inkscape:rounded")) {
1088         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1089         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1090     } else if (!strcmp(name, "inkscape:flatsided")) {
1091         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1092         char const *flatsides = repr->attribute("inkscape:flatsided");
1093         if (flatsides && !strcmp(flatsides,"false" )) {
1094             GtkToggleAction* flat2_action = GTK_TOGGLE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat2_action" ) );
1095             gtk_toggle_action_set_active( flat2_action, TRUE );
1096             gtk_action_set_sensitive( prop_action, TRUE );
1097         } else {
1098             GtkToggleAction* flat_action = GTK_TOGGLE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1099             gtk_toggle_action_set_active( flat_action, TRUE );
1100             gtk_action_set_sensitive( prop_action, FALSE );
1101         }
1102     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1103         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1104         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1105         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1106         if (r2 < r1) {
1107             gtk_adjustment_set_value(adj, r2/r1);
1108         } else {
1109             gtk_adjustment_set_value(adj, r1/r2);
1110         }
1111     } else if (!strcmp(name, "sodipodi:sides")) {
1112         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1113         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1114     }
1116     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1120 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1122     NULL, /* child_added */
1123     NULL, /* child_removed */
1124     star_tb_event_attr_changed,
1125     NULL, /* content_changed */
1126     NULL  /* order_changed */
1127 };
1130 /**
1131  *  \param selection Should not be NULL.
1132  */
1133 static void
1134 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GtkObject *tbl)
1136     int n_selected = 0;
1137     Inkscape::XML::Node *repr = NULL;
1138     Inkscape::XML::Node *oldrepr = NULL;
1140     for (GSList const *items = selection->itemList();
1141          items != NULL;
1142          items = items->next)
1143     {
1144         if (SP_IS_STAR((SPItem *) items->data)) {
1145             n_selected++;
1146             repr = SP_OBJECT_REPR((SPItem *) items->data);
1147         }
1148     }
1150     //GtkWidget *l = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(tbl), "mode_label"));
1152     if (n_selected == 0) {
1153         //gtk_label_set_markup(GTK_LABEL(l), _("<b>New:</b>"));
1154     } else if (n_selected == 1) {
1155         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1157         oldrepr = (Inkscape::XML::Node *) gtk_object_get_data(GTK_OBJECT(tbl), "repr");
1158         if (oldrepr) { // remove old listener
1159             sp_repr_remove_listener_by_data(oldrepr, tbl);
1160             Inkscape::GC::release(oldrepr);
1161             oldrepr = 0;
1162             g_object_set_data(G_OBJECT(tbl), "repr", NULL);
1163         }
1165         if (repr) {
1166             g_object_set_data(G_OBJECT(tbl), "repr", repr);
1167             Inkscape::GC::anchor(repr);
1168             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1169             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1170         }
1171     } else {
1172         // FIXME: implement averaging of all parameters for multiple selected stars
1173         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1174         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1175     }
1179 static void sp_stb_defaults( GtkWidget *widget, GtkWidget *dataKludge )
1181     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1182     // callbacks to lump all the changes for all selected objects in one undo step
1184     GtkAdjustment *adj = 0;
1186     // fixme: make settable in prefs!
1187     gint mag = 5;
1188     gdouble prop = 0.5;
1189     gboolean flat = FALSE;
1190     gdouble randomized = 0;
1191     gdouble rounded = 0;
1193     GtkToggleAction* flat_action = GTK_TOGGLE_ACTION( g_object_get_data( G_OBJECT(dataKludge), "flat_action" ) );
1194     GtkToggleAction* flat2_action = GTK_TOGGLE_ACTION( g_object_get_data( G_OBJECT(dataKludge), "flat2_action" ) );
1195     gtk_toggle_action_set_active( flat ? flat_action : flat2_action, TRUE );
1197     GtkAction* sb2 = GTK_ACTION( g_object_get_data(G_OBJECT(dataKludge), "prop_action") );
1198     gtk_action_set_sensitive( sb2, !flat );
1200     adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(dataKludge), "magnitude") );
1201     gtk_adjustment_set_value(adj, mag);
1202     gtk_adjustment_value_changed(adj);
1204     adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(dataKludge), "proportion") );
1205     gtk_adjustment_set_value(adj, prop);
1206     gtk_adjustment_value_changed(adj);
1208     adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(dataKludge), "rounded") );
1209     gtk_adjustment_set_value(adj, rounded);
1210     gtk_adjustment_value_changed(adj);
1212     adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(dataKludge), "randomized") );
1213     gtk_adjustment_set_value(adj, randomized);
1214     gtk_adjustment_value_changed(adj);
1218 void
1219 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1221     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1222     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1223     GtkWidget *l = gtk_label_new(NULL);
1224     gtk_label_set_markup(GTK_LABEL(l), title);
1225     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1226     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1227     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1231 static GtkWidget *
1232 sp_star_toolbox_new(SPDesktop *desktop)
1234     GtkWidget *toolBar = 0;
1235     GtkWidget* holder = gtk_table_new( 1, 2, FALSE );
1237     gchar const * descr =
1238         "<ui>"
1239         "  <toolbar name='StarToolbar'>"
1240         "    <separator />"
1241         "    <toolitem action='FlatAction' />"
1242         "    <toolitem action='FlatAction2' />"
1243         "    <separator />"
1244         "    <toolitem action='MagnitudeAction' />"
1245         "    <toolitem action='SpokeAction' />"
1246         "    <toolitem action='RoundednessAction' />"
1247         "    <toolitem action='RandomizationAction' />"
1248         "    <separator />"
1249         "    <toolitem action='ResetAction' />"
1250         "  </toolbar>"
1251         "</ui>";
1252     GtkUIManager* mgr = gtk_ui_manager_new();
1253     GError* errVal = 0;
1254     GtkActionGroup* mainActions = gtk_action_group_new("main");
1255 //     sp_toolbox_add_label(tbl, _("<b>New:</b>"));
1257     {
1258         gtk_object_set_data(GTK_OBJECT(holder), "dtw", desktop->canvas);
1259         gtk_object_set_data(GTK_OBJECT(holder), "desktop", desktop);
1261         //EgeAdjustmentAction* calligraphy_angle = 0;
1262         EgeAdjustmentAction* eact = 0;
1263         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1264         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1266         /* Flatsided checkbox */
1267         {
1268             InkRadioAction* act = ink_radio_action_new( "FlatAction",
1269                                                         _("Polygon"),
1270                                                         _("Regular polygon (with one handle) instead of a star"),
1271                                                         "star_flat",
1272                                                         Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1273             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1275             InkRadioAction* act2 = ink_radio_action_new( "FlatAction2",
1276                                                          _("Star"),
1277                                                          _("Star instead of a regular polygon (with one handle)"),
1278                                                          "star_angled",
1279                                                          Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1280             gtk_action_group_add_action( mainActions, GTK_ACTION( act2 ) );
1282             // Connect to be in the same radio button group.
1283             gtk_radio_action_set_group( GTK_RADIO_ACTION(act), 0 );
1284             GSList* radioGroup = gtk_radio_action_get_group( GTK_RADIO_ACTION(act) );
1285             gtk_radio_action_set_group( GTK_RADIO_ACTION(act2), radioGroup );
1287             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), isFlatSided );
1288             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act2), !isFlatSided );
1290             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1291             gtk_object_set_data( GTK_OBJECT(holder), "flat_action", act );
1292             gtk_object_set_data( GTK_OBJECT(holder), "flat2_action", act2 );
1293         }
1295         /* Magnitude */
1296         //gchar const* labels[] = {_("tri (default)"), _("quad"), _("pent")};
1297         //gdouble values[] = {3, 4, 5};
1298         eact = create_adjustment_action( "MagnitudeAction",
1299                                          _("Corners:"), _("Number of corners of a polygon or star"),
1300                                          "tools.shapes.star", "magnitude", 3,
1301                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1302                                          3, 1024, 1, 5,
1303                                          0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
1304                                          sp_stb_magnitude_value_changed,
1305                                          1.0, 0 );
1306         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1307         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1309         /* Spoke ratio */
1310         //gchar const* labels2[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1311         //gdouble values2[] = {-90, 0, 30, 90};
1312         eact = create_adjustment_action( "SpokeAction",
1313                                          _("Spoke ratio:"),
1314                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1315                                          // Base radius is the same for the closest handle.
1316                                          _("Base radius to tip radius ratio"),
1317                                          "tools.shapes.star", "proportion", 0.5,
1318                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1319                                          0.01, 1.0, 0.01, 0.1,
1320                                          0, 0, 0, // labels2, values2, G_N_ELEMENTS(labels2),
1321                                          sp_stb_proportion_value_changed );
1322         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1323         g_object_set_data(G_OBJECT(holder), "prop_action", eact);
1325         if ( !isFlatSided ) {
1326             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1327         } else {
1328             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1329         }
1331         /* Roundedness */
1332         //gchar const* labels3[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1333         //gdouble values3[] = {-90, 0, 30, 90};
1334         eact = create_adjustment_action( "RoundednessAction",
1335                                          _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1336                                          "tools.shapes.star", "rounded", 0.0,
1337                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1338                                          -100.0, 100.0, 0.01, 0.1,
1339                                          0, 0, 0, // labels3, values3, G_N_ELEMENTS(labels3),
1340                                          sp_stb_rounded_value_changed );
1341         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1342         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1344         /* Randomization */
1345         //gchar const* labels4[] = {_("(left edge up)"), _("(horizontal)"), _("(default)"), _("(right edge up)")};
1346         //gdouble values4[] = {-90, 0, 30, 90};
1347         eact = create_adjustment_action( "RandomizationAction",
1348                                          _("Randomized:"), _("Scatter randomly the corners and angles"),
1349                                          "tools.shapes.star", "randomized", 0.0,
1350                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1351                                          -10.0, 10.0, 0.001, 0.01,
1352                                          0, 0, 0, // labels4, values4, G_N_ELEMENTS(labels4),
1353                                          sp_stb_randomized_value_changed, 0.1, 3 );
1354         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1355         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1356     }
1358     {
1359         /* Reset */
1360         {
1361             GtkAction* act = gtk_action_new( "ResetAction",
1362                                              _("Defaults"),
1363                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1364                                              GTK_STOCK_CLEAR );
1365             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1366             gtk_action_group_add_action( mainActions, act );
1367             gtk_action_set_sensitive( act, TRUE );
1368         }
1369     }
1371     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1372     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1374     toolBar = gtk_ui_manager_get_widget( mgr, "/ui/StarToolbar" );
1375     gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1376     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), GTK_ICON_SIZE_SMALL_TOOLBAR );
1379     gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1381     Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of new stars"));
1382     swatch->setDesktop( desktop );
1383     swatch->setClickVerb( SP_VERB_CONTEXT_STAR_PREFS );
1384     swatch->setWatchedTool( "tools.shapes.star", true );
1385     GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
1386     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 );
1388     gtk_widget_show_all(holder);
1389     //sp_set_font_size_smaller (tbl);
1391     sigc::connection *connection = new sigc::connection(
1392         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GtkObject *)holder))
1393         );
1394     g_signal_connect(G_OBJECT(holder), "destroy", G_CALLBACK(delete_connection), connection);
1396     return holder;
1400 //########################
1401 //##       Rect         ##
1402 //########################
1404 static void
1405 sp_rtb_sensitivize (GtkWidget *tbl)
1407     GtkAdjustment *adj1 = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "rx"));
1408     GtkAdjustment *adj2 = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "ry"));
1409     GtkWidget *not_rounded = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "not_rounded");
1411     if (adj1->value == 0 && adj2->value == 0 && gtk_object_get_data(GTK_OBJECT(tbl), "single")) { // only for a single selected rect (for now)
1412         gtk_widget_set_sensitive(GTK_WIDGET(not_rounded), FALSE);
1413     } else {
1414         gtk_widget_set_sensitive(GTK_WIDGET(not_rounded), TRUE);
1415     }
1419 static void
1420 sp_rtb_value_changed(GtkAdjustment *adj, GtkWidget *tbl, gchar const *value_name,
1421                           void (*setter)(SPRect *, gdouble))
1423     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
1425     GtkWidget *us = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(tbl), "units");
1426     SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
1428     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1429         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1430     }
1432     // quit if run by the attr_changed listener
1433     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1434         return;
1435     }
1437     // in turn, prevent listener from responding
1438     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1440     bool modmade = false;
1441     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1442     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1443         if (SP_IS_RECT(items->data)) {
1444             if (adj->value != 0) {
1445                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
1446             } else {
1447                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
1448             }
1449             modmade = true;
1450         }
1451     }
1453     sp_rtb_sensitivize (tbl);
1455     if (modmade) {
1456         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
1457                                    _("Change rectangle"));
1458     }
1460     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1462     // defocus spinbuttons by moving focus to the canvas, unless "stay" is on
1463     spinbutton_defocus(GTK_OBJECT(tbl));
1466 static void
1467 sp_rtb_rx_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
1469     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
1472 static void
1473 sp_rtb_ry_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
1475     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
1478 static void
1479 sp_rtb_width_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
1481     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
1484 static void
1485 sp_rtb_height_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
1487     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
1492 static void
1493 sp_rtb_defaults( GtkWidget *widget, GtkObject *obj)
1495     GtkWidget *tbl = GTK_WIDGET(obj);
1497     GtkAdjustment *adj;
1499     adj = (GtkAdjustment*)gtk_object_get_data(obj, "rx");
1500     gtk_adjustment_set_value(adj, 0.0);
1501     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
1502     gtk_adjustment_value_changed(adj);
1504     adj = (GtkAdjustment*)gtk_object_get_data(obj, "ry");
1505     gtk_adjustment_set_value(adj, 0.0);
1506     gtk_adjustment_value_changed(adj);
1508     sp_rtb_sensitivize (tbl);
1510     spinbutton_defocus(GTK_OBJECT(tbl));
1513 static void rect_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1514                                        gchar const *old_value, gchar const *new_value,
1515                                        bool is_interactive, gpointer data)
1517     GtkWidget *tbl = GTK_WIDGET(data);
1519     // quit if run by the _changed callbacks
1520     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1521         return;
1522     }
1524     // in turn, prevent callbacks from responding
1525     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1527     GtkWidget *us = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(tbl), "units");
1528     SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
1530     SPItem *item = SP_ITEM(g_object_get_data(G_OBJECT(tbl), "item"));
1531     if (SP_IS_RECT(item)) {
1532         {
1533             GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "rx");
1534             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
1535             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
1536         }
1538         {
1539             GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "ry");
1540             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
1541             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
1542         }
1544         {
1545             GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "width");
1546             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
1547             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
1548         }
1550         {
1551             GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "height");
1552             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
1553             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
1554         }
1555     }
1557     sp_rtb_sensitivize (tbl);
1559     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1563 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
1564     NULL, /* child_added */
1565     NULL, /* child_removed */
1566     rect_tb_event_attr_changed,
1567     NULL, /* content_changed */
1568     NULL  /* order_changed */
1569 };
1571 /**
1572  *  \param selection should not be NULL.
1573  */
1574 static void
1575 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GtkObject *tbl)
1577     int n_selected = 0;
1578     Inkscape::XML::Node *repr = NULL;
1579     SPItem *item = NULL;
1580     Inkscape::XML::Node *oldrepr = NULL;
1582     for (GSList const *items = selection->itemList();
1583          items != NULL;
1584          items = items->next) {
1585         if (SP_IS_RECT((SPItem *) items->data)) {
1586             n_selected++;
1587             item = (SPItem *) items->data;
1588             repr = SP_OBJECT_REPR(item);
1589         }
1590     }
1592     GtkWidget *l = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(tbl), "mode_label"));
1594     g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(FALSE));
1596     if (n_selected == 0) {
1597         gtk_label_set_markup(GTK_LABEL(l), _("<b>New:</b>"));
1599         GtkWidget *w = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(tbl), "width_sb");
1600         gtk_widget_set_sensitive(w, FALSE);
1601         GtkWidget *h = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(tbl), "height_sb");
1602         gtk_widget_set_sensitive(h, FALSE);
1604     } else if (n_selected == 1) {
1605         gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1606         g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE));
1608         GtkWidget *w = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(tbl), "width_sb");
1609         gtk_widget_set_sensitive(w, TRUE);
1610         GtkWidget *h = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(tbl), "height_sb");
1611         gtk_widget_set_sensitive(h, TRUE);
1613         oldrepr = (Inkscape::XML::Node *) gtk_object_get_data(GTK_OBJECT(tbl), "repr");
1614         if (oldrepr) { // remove old listener
1615             sp_repr_remove_listener_by_data(oldrepr, tbl);
1616             Inkscape::GC::release(oldrepr);
1617             oldrepr = 0;
1618             g_object_set_data(G_OBJECT(tbl), "repr", NULL);
1619         }
1620         if (repr) {
1621             g_object_set_data(G_OBJECT(tbl), "repr", repr);
1622             g_object_set_data(G_OBJECT(tbl), "item", item);
1623             Inkscape::GC::anchor(repr);
1624             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
1625             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
1626         }
1627     } else {
1628         // FIXME: implement averaging of all parameters for multiple selected
1629         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1630         gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1631         sp_rtb_sensitivize (GTK_WIDGET(tbl));
1632     }
1636 static GtkWidget *
1637 sp_rect_toolbox_new(SPDesktop *desktop)
1639     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1641     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1642     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1644     GtkTooltips *tt = gtk_tooltips_new();
1646     sp_toolbox_add_label(tbl, _("<b>New:</b>"));
1648     // rx/ry units menu: create
1649     GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
1650     sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
1651     sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
1652     // fixme: add % meaning per cent of the width/height
1654     /* W */
1655     {
1656         GtkWidget *hb = sp_tb_spinbutton(_("W:"), _("Width of rectangle"),
1657                                          "tools.shapes.rect", "width", 0,
1658                                          us, tbl, TRUE, "altx-rect",
1659                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1660                                          sp_rtb_width_value_changed);
1661         gtk_object_set_data(GTK_OBJECT(tbl), "width_sb", hb);
1662         gtk_widget_set_sensitive(hb, FALSE);
1663         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
1664     }
1666     /* H */
1667     {
1668         GtkWidget *hb = sp_tb_spinbutton(_("H:"), _("Height of rectangle"),
1669                                          "tools.shapes.rect", "height", 0,
1670                                          us, tbl, FALSE, NULL,
1671                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1672                                          sp_rtb_height_value_changed);
1673         gtk_object_set_data(GTK_OBJECT(tbl), "height_sb", hb);
1674         gtk_widget_set_sensitive(hb, FALSE);
1675         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
1676     }
1678     /* rx */
1679     {
1680         GtkWidget *hb = sp_tb_spinbutton(_("Rx:"), _("Horizontal radius of rounded corners"),
1681                                          "tools.shapes.rect", "rx", 0,
1682                                          us, tbl, FALSE, NULL,
1683                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1684                                          sp_rtb_rx_value_changed);
1685         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
1686     }
1688     /* ry */
1689     {
1690         GtkWidget *hb = sp_tb_spinbutton(_("Ry:"), _("Vertical radius of rounded corners"),
1691                                          "tools.shapes.rect", "ry", 0,
1692                                          us, tbl, FALSE, NULL,
1693                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1694                                          sp_rtb_ry_value_changed);
1695         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
1696     }
1698     // add the units menu
1699     gtk_widget_show(us);
1700     gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
1701     gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
1703     /* Reset */
1704     {
1705         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1706         GtkWidget *b = gtk_button_new_with_label(_("Not rounded"));
1707         gtk_object_set_data(GTK_OBJECT(tbl), "not_rounded", b);
1708         gtk_tooltips_set_tip(tt, b, _("Make corners sharp"), NULL);
1709         gtk_widget_show(b);
1710         gtk_container_add(GTK_CONTAINER(hb), b);
1711         gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_rtb_defaults), tbl);
1712         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
1713     }
1715     Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of new rectangles"));
1716     swatch->setDesktop (desktop);
1717     swatch->setClickVerb (SP_VERB_CONTEXT_RECT_PREFS);
1718     swatch->setWatchedTool ("tools.shapes.rect", true);
1719     GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
1720     gtk_box_pack_end(GTK_BOX(tbl), swatch_, FALSE, FALSE, 0);
1722     g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE));
1723     sp_rtb_sensitivize (tbl);
1725     gtk_widget_show_all(tbl);
1726     sp_set_font_size_smaller (tbl);
1728     sigc::connection *connection = new sigc::connection(
1729         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GtkObject *)tbl))
1730         );
1731     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection), connection);
1733     return tbl;
1736 //########################
1737 //##       Spiral       ##
1738 //########################
1740 static void
1741 sp_spl_tb_value_changed(GtkAdjustment *adj, GtkWidget *tbl, gchar const *value_name)
1743     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
1745     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1746         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
1747     }
1749     // quit if run by the attr_changed listener
1750     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1751         return;
1752     }
1754     // in turn, prevent listener from responding
1755     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1757     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
1759     bool modmade = false;
1760     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
1761          items != NULL;
1762          items = items->next)
1763     {
1764         if (SP_IS_SPIRAL((SPItem *) items->data)) {
1765             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1766             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
1767             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1768             modmade = true;
1769         }
1770     }
1772     g_free(namespaced_name);
1774     if (modmade) {
1775         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
1776                                    _("Change spiral"));
1777     }
1779     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1781     spinbutton_defocus(GTK_OBJECT(tbl));
1784 static void
1785 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
1787     sp_spl_tb_value_changed(adj, tbl, "revolution");
1790 static void
1791 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
1793     sp_spl_tb_value_changed(adj, tbl, "expansion");
1796 static void
1797 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
1799     sp_spl_tb_value_changed(adj, tbl, "t0");
1802 static void
1803 sp_spl_tb_defaults(GtkWidget *widget, GtkObject *obj)
1805     GtkWidget *tbl = GTK_WIDGET(obj);
1807     GtkAdjustment *adj;
1809     // fixme: make settable
1810     gdouble rev = 5;
1811     gdouble exp = 1.0;
1812     gdouble t0 = 0.0;
1814     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
1815     gtk_adjustment_set_value(adj, rev);
1816     gtk_adjustment_value_changed(adj);
1818     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
1819     gtk_adjustment_set_value(adj, exp);
1820     gtk_adjustment_value_changed(adj);
1822     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
1823     gtk_adjustment_set_value(adj, t0);
1824     gtk_adjustment_value_changed(adj);
1826     spinbutton_defocus(GTK_OBJECT(tbl));
1830 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1831                                          gchar const *old_value, gchar const *new_value,
1832                                          bool is_interactive, gpointer data)
1834     GtkWidget *tbl = GTK_WIDGET(data);
1836     // quit if run by the _changed callbacks
1837     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1838         return;
1839     }
1841     // in turn, prevent callbacks from responding
1842     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1844     GtkAdjustment *adj;
1845     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
1846     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
1848     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
1849     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
1851     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
1852     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
1854     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1858 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
1859     NULL, /* child_added */
1860     NULL, /* child_removed */
1861     spiral_tb_event_attr_changed,
1862     NULL, /* content_changed */
1863     NULL  /* order_changed */
1864 };
1866 static void
1867 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GtkObject *tbl)
1869     int n_selected = 0;
1870     Inkscape::XML::Node *repr = NULL;
1871     Inkscape::XML::Node *oldrepr = NULL;
1873     for (GSList const *items = selection->itemList();
1874          items != NULL;
1875          items = items->next)
1876     {
1877         if (SP_IS_SPIRAL((SPItem *) items->data)) {
1878             n_selected++;
1879             repr = SP_OBJECT_REPR((SPItem *) items->data);
1880         }
1881     }
1883     GtkWidget *l = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(tbl), "mode_label"));
1885     if (n_selected == 0) {
1886         gtk_label_set_markup(GTK_LABEL(l), _("<b>New:</b>"));
1887     } else if (n_selected == 1) {
1888         gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1890         oldrepr = (Inkscape::XML::Node *) gtk_object_get_data(GTK_OBJECT(tbl), "repr");
1891         if (oldrepr) { // remove old listener
1892             sp_repr_remove_listener_by_data(oldrepr, tbl);
1893             Inkscape::GC::release(oldrepr);
1894             oldrepr = 0;
1895             g_object_set_data(G_OBJECT(tbl), "repr", NULL);
1896         }
1898         if (repr) {
1899             g_object_set_data(G_OBJECT(tbl), "repr", repr);
1900             Inkscape::GC::anchor(repr);
1901             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
1902             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
1903         }
1904     } else {
1905         // FIXME: implement averaging of all parameters for multiple selected
1906         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1907         gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1908     }
1912 static GtkWidget *
1913 sp_spiral_toolbox_new(SPDesktop *desktop)
1915     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1916     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1917     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1919     GtkTooltips *tt = gtk_tooltips_new();
1921     sp_toolbox_add_label(tbl, _("<b>New:</b>"));
1923     /* Revolution */
1924     {
1925         GtkWidget *hb = sp_tb_spinbutton(_("Turns:"), _("Number of revolutions"),
1926                                          "tools.shapes.spiral", "revolution", 3.0,
1927                                          NULL, tbl, TRUE, "altx-spiral",
1928                                          0.01, 1024.0, 0.1, 1.0,
1929                                          sp_spl_tb_revolution_value_changed, 1, 2);
1930         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
1931     }
1933     /* Expansion */
1934     {
1935         GtkWidget *hb = sp_tb_spinbutton(_("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
1936                                          "tools.shapes.spiral", "expansion", 1.0,
1937                                          NULL, tbl, FALSE, NULL,
1938                                          0.0, 1000.0, 0.01, 1.0,
1939                                          sp_spl_tb_expansion_value_changed);
1940         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
1941     }
1943     /* T0 */
1944     {
1945         GtkWidget *hb = sp_tb_spinbutton(_("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
1946                                          "tools.shapes.spiral", "t0", 0.0,
1947                                          NULL, tbl, FALSE, NULL,
1948                                          0.0, 0.999, 0.01, 1.0,
1949                                          sp_spl_tb_t0_value_changed);
1950         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_SPACING);
1951     }
1953     aux_toolbox_space(tbl, AUX_SPACING);
1955     /* Reset */
1956     {
1957         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1958         GtkWidget *b = gtk_button_new_with_label(_("Defaults"));
1959         gtk_tooltips_set_tip(tt, b, _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"), NULL);
1960         gtk_widget_show(b);
1961         gtk_container_add(GTK_CONTAINER(hb), b);
1962         gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_spl_tb_defaults), tbl);
1963         gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
1964     }
1966     Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of new spirals"));
1967     swatch->setDesktop (desktop);
1968     swatch->setClickVerb (SP_VERB_CONTEXT_SPIRAL_PREFS);
1969     swatch->setWatchedTool ("tools.shapes.spiral", true);
1970     GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
1971     gtk_box_pack_end(GTK_BOX(tbl), swatch_, FALSE, FALSE, 0);
1973     gtk_widget_show_all(tbl);
1974     sp_set_font_size_smaller (tbl);
1976     sigc::connection *connection = new sigc::connection(
1977         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GtkObject *)tbl))
1978         );
1979     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection), connection);
1981     return tbl;
1984 //########################
1985 //##     Pen/Pencil    ##
1986 //########################
1989 static GtkWidget *
1990 sp_pen_toolbox_new(SPDesktop *desktop)
1992     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1993     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1994     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1996     Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of new paths created by Pen"));
1997     swatch->setDesktop (desktop);
1998     swatch->setClickVerb (SP_VERB_CONTEXT_PEN_PREFS);
1999     swatch->setWatchedTool ("tools.freehand.pen", true);
2000     GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
2001     gtk_box_pack_end(GTK_BOX(tbl), swatch_, FALSE, FALSE, 0);
2003     gtk_widget_show_all(tbl);
2004     sp_set_font_size_smaller (tbl);
2006     return tbl;
2009 static GtkWidget *
2010 sp_pencil_toolbox_new(SPDesktop *desktop)
2012     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
2013     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2014     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2016     Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of new paths created by Pencil"));
2017     swatch->setDesktop (desktop);
2018     swatch->setClickVerb (SP_VERB_CONTEXT_PENCIL_PREFS);
2019     swatch->setWatchedTool ("tools.freehand.pencil", true);
2020     GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
2021     gtk_box_pack_end(GTK_BOX(tbl), swatch_, FALSE, FALSE, 0);
2023     gtk_widget_show_all(tbl);
2024     sp_set_font_size_smaller (tbl);
2026     return tbl;
2030 //########################
2031 //##     Calligraphy    ##
2032 //########################
2034 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GtkWidget *tbl )
2036     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
2039 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GtkWidget *tbl )
2041     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
2044 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GtkWidget *tbl )
2046     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
2049 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GtkWidget *tbl )
2051     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
2054 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GtkWidget *tbl )
2056     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
2059 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GtkWidget *tbl )
2061     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
2064 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GtkWidget *tbl )
2066     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
2069 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GtkWidget *tbl )
2071     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
2074 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer data )
2076     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2079 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
2081     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
2083     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
2086 static void sp_ddc_defaults(GtkWidget *, GtkWidget *dataKludge)
2088     // FIXME: make defaults settable via Inkscape Options
2089     struct KeyValue {
2090         char const *key;
2091         double value;
2092     } const key_values[] = {
2093         {"mass", 0.02},
2094         {"wiggle", 0.0},
2095         {"angle", 30.0},
2096         {"width", 15},
2097         {"thinning", 0.1},
2098         {"tremor", 0.0},
2099         {"flatness", 0.9},
2100         {"cap_rounding", 0.0}
2101     };
2103     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
2104         KeyValue const &kv = key_values[i];
2105         GtkAdjustment* adj = static_cast<GtkAdjustment *>(gtk_object_get_data(GTK_OBJECT(dataKludge), kv.key));
2106         if ( adj ) {
2107             gtk_adjustment_set_value(adj, kv.value);
2108         }
2109     }
2113 static GtkWidget *
2114 sp_calligraphy_toolbox_new(SPDesktop *desktop)
2116     GtkWidget *toolBar = 0;
2117     GtkWidget* holder = gtk_table_new( 1, 2, FALSE );
2119     gchar const * descr =
2120         "<ui>"
2121         "  <toolbar name='CalligraphyToolbar'>"
2122         "    <separator />"
2123         "    <toolitem name='Width' action='WidthAction' />"
2124         "    <toolitem name='Thinning' action='ThinningAction' />"
2125         "    <separator />"
2126         "    <toolitem name='Angle' action='AngleAction' />"
2127         "    <toolitem action='FixationAction' />"
2128         "    <toolitem action='CapRoundingAction' />"
2129         "    <separator />"
2130         "    <toolitem action='TremorAction' />"
2131         "    <toolitem action='WiggleAction' />"
2132         "    <toolitem action='MassAction' />"
2133         "    <separator />"
2134         "    <toolitem action='PressureAction' />"
2135         "    <toolitem action='TiltAction' />"
2136         "    <toolitem action='ResetAction' />"
2137         "  </toolbar>"
2138         "</ui>";
2139     GtkUIManager* mgr = gtk_ui_manager_new();
2140     GError* errVal = 0;
2141     GtkActionGroup* mainActions = gtk_action_group_new("main");
2143     {
2144         EgeAdjustmentAction* calligraphy_angle = 0;
2146         {
2147         /* Width */
2148         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
2149         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2150         EgeAdjustmentAction *eact = create_adjustment_action( "WidthAction",
2151                                          _("Width:"), _("The width of the calligraphic pen (relative to the visible canvas area)"),
2152                                          "tools.calligraphic", "width", 15,
2153                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
2154                                          1, 100, 1.0, 10.0,
2155                                          labels, values, G_N_ELEMENTS(labels),
2156                                          sp_ddc_width_value_changed,  0.01, 0, 100 );
2157         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2158         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2159         }
2161         {
2162         /* Thinning */
2163             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
2164             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, -0.2, 0.4, 1};
2165         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
2166                                          _("Thinning:"), _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
2167                                          "tools.calligraphic", "thinning", 0.1,
2168                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2169                                          -1.0, 1.0, 0.01, 0.1,
2170                                          labels, values, G_N_ELEMENTS(labels),
2171                                          sp_ddc_velthin_value_changed, 0.01, 2);
2172         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2173         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2174         }
2176         {
2177         /* Angle */
2178         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
2179         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2180         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
2181                                          _("Angle:"), _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
2182                                          "tools.calligraphic", "angle", 30,
2183                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
2184                                          -90.0, 90.0, 1.0, 10.0,
2185                                          labels, values, G_N_ELEMENTS(labels),
2186                                          sp_ddc_angle_value_changed, 1, 0 );
2187         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2188         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2189         calligraphy_angle = eact;
2190         }
2192         {
2193         /* Fixation */
2194             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
2195         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
2196         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
2197                                          _("Fixation:"), _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
2198                                          "tools.calligraphic", "flatness", 0.9,
2199                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2200                                          0.0, 1.0, 0.01, 0.1,
2201                                          labels, values, G_N_ELEMENTS(labels),
2202                                          sp_ddc_flatness_value_changed, 0.01, 2 );
2203         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2204         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2205         }
2207         {
2208         /* Cap Rounding */
2209             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
2210         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
2211         // TRANSLATORS: "cap" means "end" (both start and finish) here
2212         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
2213                                          _("Caps:"), _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
2214                                          "tools.calligraphic", "cap_rounding", 0.0,
2215                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2216                                          0.0, 5.0, 0.01, 0.1,
2217                                          labels, values, G_N_ELEMENTS(labels),
2218                                          sp_ddc_cap_rounding_value_changed, 0.01, 2 );
2219         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2220         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2221         }
2223         {
2224         /* Tremor */
2225             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
2226         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
2227         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
2228                                          _("Tremor:"), _("Increase to make strokes rugged and trembling"),
2229                                          "tools.calligraphic", "tremor", 0.0,
2230                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2231                                          0.0, 1.0, 0.01, 0.1,
2232                                          labels, values, G_N_ELEMENTS(labels),
2233                                          sp_ddc_tremor_value_changed, 0.01, 2 );
2235         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2236         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2237         }
2239         {
2240         /* Wiggle */
2241         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
2242         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
2243         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
2244                                          _("Wiggle:"), _("Increase to make the pen waver and wiggle"),
2245                                          "tools.calligraphic", "wiggle", 0.0,
2246                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2247                                          0.0, 1.0, 0.01, 0.1,
2248                                          labels, values, G_N_ELEMENTS(labels),
2249                                          sp_ddc_wiggle_value_changed, 0.01, 2 );
2250         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2251         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2252         }
2254         {
2255         /* Mass */
2256             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
2257         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
2258         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
2259                                          _("Mass:"), _("Increase to make the pen drag behind, as if slowed by inertia"),
2260                                          "tools.calligraphic", "mass", 0.02,
2261                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2262                                          0.0, 1.0, 0.01, 0.1,
2263                                          labels, values, G_N_ELEMENTS(labels),
2264                                          sp_ddc_mass_value_changed, 0.01, 2 );
2265         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2266         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2267         }
2270         /* Use Pressure button */
2271         {
2272             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
2273                                                           _("Pressure"),
2274                                                           _("Use the pressure of the input device to alter the width of the pen"),
2275                                                           "use_pressure",
2276                                                           Inkscape::ICON_SIZE_DECORATION );
2277             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2278             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
2279             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
2280         }
2282         /* Use Tilt button */
2283         {
2284             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
2285                                                           _("Tilt"),
2286                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
2287                                                           "use_tilt",
2288                                                           Inkscape::ICON_SIZE_DECORATION );
2289             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2290             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
2291             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2292             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
2293         }
2295         /* Reset */
2296         {
2297             GtkAction* act = gtk_action_new( "ResetAction",
2298                                              _("Defaults"),
2299                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2300                                              GTK_STOCK_CLEAR );
2301             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
2302             gtk_action_group_add_action( mainActions, act );
2303             gtk_action_set_sensitive( act, TRUE );
2304         }
2305     }
2308     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
2309     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2311     toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CalligraphyToolbar" );
2312     gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2313     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), GTK_ICON_SIZE_SMALL_TOOLBAR );
2316     gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
2318     Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of new calligraphic strokes"));
2319     swatch->setDesktop (desktop);
2320     swatch->setClickVerb (SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS);
2321     swatch->setWatchedTool ("tools.calligraphic", true);
2322     GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
2323     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 );
2325     gtk_widget_show_all(holder);
2326     //sp_set_font_size_smaller (tbl);
2328     return holder;
2332 //########################
2333 //##    Circle / Arc    ##
2334 //########################
2336 static void
2337 sp_arctb_sensitivize (GtkWidget *tbl, double v1, double v2)
2339     GtkWidget *ocb = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "open_checkbox");
2340     GtkWidget *make_whole = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "make_whole");
2342     if (v1 == 0 && v2 == 0) {
2343         if (gtk_object_get_data(GTK_OBJECT(tbl), "single")) { // only for a single selected ellipse (for now)
2344             gtk_widget_set_sensitive(GTK_WIDGET(ocb), FALSE);
2345             gtk_widget_set_sensitive(GTK_WIDGET(make_whole), FALSE);
2346         }
2347     } else {
2348         gtk_widget_set_sensitive(GTK_WIDGET(ocb), TRUE);
2349         gtk_widget_set_sensitive(GTK_WIDGET(make_whole), TRUE);
2350     }
2353 static void
2354 sp_arctb_startend_value_changed(GtkAdjustment *adj, GtkWidget *tbl, gchar const *value_name, gchar const *other_name)
2356     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
2358     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2359         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
2360     }
2362     // quit if run by the attr_changed listener
2363     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2364         return;
2365     }
2367     // in turn, prevent listener from responding
2368     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2370     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2372     bool modmade = false;
2373     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2374          items != NULL;
2375          items = items->next)
2376     {
2377         SPItem *item = SP_ITEM(items->data);
2379         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
2381             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
2382             SPArc *arc = SP_ARC(item);
2384             if (!strcmp(value_name, "start"))
2385                 ge->start = (adj->value * M_PI)/ 180;
2386             else
2387                 ge->end = (adj->value * M_PI)/ 180;
2389             sp_genericellipse_normalize(ge);
2390             ((SPObject *)arc)->updateRepr();
2391             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
2393             modmade = true;
2394         }
2395     }
2397     g_free(namespaced_name);
2399     GtkAdjustment *other = (GtkAdjustment *)gtk_object_get_data(GTK_OBJECT(tbl), other_name);
2401     sp_arctb_sensitivize (tbl, adj->value, other->value);
2403     if (modmade) {
2404         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
2405                                    _("Arc: Change start/end"));
2406     }
2408     // defocus spinbuttons by moving focus to the canvas, unless "stay" is on
2409     spinbutton_defocus(GTK_OBJECT(tbl));
2411     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2415 static void
2416 sp_arctb_start_value_changed(GtkAdjustment *adj,  GtkWidget *tbl)
2418     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
2421 static void
2422 sp_arctb_end_value_changed(GtkAdjustment *adj, GtkWidget *tbl)
2424     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
2427 static void
2428 sp_arctb_open_state_changed(GtkWidget *widget, GtkObject *tbl)
2430     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
2432     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2433         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
2434             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
2435         } else {
2436             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
2437         }
2438     }
2440     // quit if run by the attr_changed listener
2441     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2442         return;
2443     }
2445     // in turn, prevent listener from responding
2446     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2448     bool modmade = false;
2450     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
2451         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2452              items != NULL;
2453              items = items->next)
2454         {
2455             if (SP_IS_ARC((SPItem *) items->data)) {
2456                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2457                 repr->setAttribute("sodipodi:open", "true");
2458                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2459                 modmade = true;
2460             }
2461         }
2462     } else {
2463         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2464              items != NULL;
2465              items = items->next)
2466         {
2467             if (SP_IS_ARC((SPItem *) items->data))    {
2468                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2469                 repr->setAttribute("sodipodi:open", NULL);
2470                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2471                 modmade = true;
2472             }
2473         }
2474     }
2476     if (modmade) {
2477         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
2478                                    _("Arc: Change open/closed"));
2479     }
2481     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2483     spinbutton_defocus(GTK_OBJECT(tbl));
2486 static void sp_arctb_defaults(GtkWidget *, GtkObject *obj)
2488     GtkWidget *tbl = GTK_WIDGET(obj);
2490     GtkAdjustment *adj;
2491     adj = (GtkAdjustment*)gtk_object_get_data(obj, "start");
2492     gtk_adjustment_set_value(adj, 0.0);
2493     gtk_adjustment_value_changed(adj);
2495     adj = (GtkAdjustment*)gtk_object_get_data(obj, "end");
2496     gtk_adjustment_set_value(adj, 0.0);
2497     gtk_adjustment_value_changed(adj);
2499     spinbutton_defocus(GTK_OBJECT(tbl));
2502 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2503                                       gchar const *old_value, gchar const *new_value,
2504                                       bool is_interactive, gpointer data)
2506     GtkWidget *tbl = GTK_WIDGET(data);
2508     // quit if run by the _changed callbacks
2509     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2510         return;
2511     }
2513     // in turn, prevent callbacks from responding
2514     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2516     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
2517     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
2519     GtkAdjustment *adj1,*adj2;
2520     adj1 = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "start");
2521     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
2522     adj2 = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "end");
2523     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
2525     sp_arctb_sensitivize (tbl, adj1->value, adj2->value);
2527     char const *openstr = NULL;
2528     openstr = repr->attribute("sodipodi:open");
2529     GtkWidget *ocb = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "open_checkbox");
2531     if (openstr) {
2532         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ocb),  TRUE);
2533     } else {
2534         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(ocb),  FALSE);
2535     }
2537     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2540 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
2541     NULL, /* child_added */
2542     NULL, /* child_removed */
2543     arc_tb_event_attr_changed,
2544     NULL, /* content_changed */
2545     NULL  /* order_changed */
2546 };
2549 static void
2550 sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GtkObject *tbl)
2552     int n_selected = 0;
2553     Inkscape::XML::Node *repr = NULL;
2554     Inkscape::XML::Node *oldrepr = NULL;
2556     for (GSList const *items = selection->itemList();
2557          items != NULL;
2558          items = items->next)
2559     {
2560         if (SP_IS_ARC((SPItem *) items->data)) {
2561             n_selected++;
2562             repr = SP_OBJECT_REPR((SPItem *) items->data);
2563         }
2564     }
2566     GtkWidget *l = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(tbl), "mode_label"));
2568     g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(FALSE));
2569     if (n_selected == 0) {
2570         gtk_label_set_markup(GTK_LABEL(l), _("<b>New:</b>"));
2571     } else if (n_selected == 1) {
2572         g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE));
2573         gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2575         oldrepr = (Inkscape::XML::Node *) gtk_object_get_data(GTK_OBJECT(tbl), "repr");
2577         if (oldrepr) { // remove old listener
2578             sp_repr_remove_listener_by_data(oldrepr, tbl);
2579             Inkscape::GC::release(oldrepr);
2580             oldrepr = 0;
2581             g_object_set_data(G_OBJECT(tbl), "repr", NULL);
2582         }
2584         if (repr) {
2585             g_object_set_data(G_OBJECT(tbl), "repr", repr);
2586             Inkscape::GC::anchor(repr);
2587             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
2588             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
2589         }
2590     } else {
2591         // FIXME: implement averaging of all parameters for multiple selected
2592         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2593         gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2594         sp_arctb_sensitivize (GTK_WIDGET(tbl), 1, 0);
2595     }
2599 static GtkWidget *
2600 sp_arc_toolbox_new(SPDesktop *desktop)
2602     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
2604     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2605     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2607     GtkTooltips *tt = gtk_tooltips_new();
2609     sp_toolbox_add_label(tbl, _("<b>New:</b>"));
2611     /* Start */
2612     {
2613         GtkWidget *hb = sp_tb_spinbutton(_("Start:"), _("The angle (in degrees) from the horizontal to the arc's start point"),
2614                                          "tools.shapes.arc", "start", 0.0,
2615                                          NULL, tbl, TRUE, "altx-arc",
2616                                          -360.0, 360.0, 1.0, 10.0,
2617                                          sp_arctb_start_value_changed);
2618         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
2619     }
2621     /* End */
2622     {
2623         GtkWidget *hb = sp_tb_spinbutton(_("End:"), _("The angle (in degrees) from the horizontal to the arc's end point"),
2624                                          "tools.shapes.arc", "end", 0.0,
2625                                          NULL, tbl, FALSE, NULL,
2626                                          -360.0, 360.0, 1.0, 10.0,
2627                                          sp_arctb_end_value_changed);
2628         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
2629     }
2631     /* Segments / Pie checkbox */
2632     {
2633         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
2634         GtkWidget *fscb = gtk_check_button_new_with_label(_("Open arc"));
2635         gtk_tooltips_set_tip(tt, fscb, _("Switch between arc (unclosed shape) and segment (closed shape with two radii)"), NULL);
2637         gchar const *openstr = NULL;
2638         openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
2639         if (!openstr || (openstr && !strcmp(openstr, "false")))
2640             gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb),  FALSE);
2641         else
2642             gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(fscb),  TRUE);
2644         gtk_widget_show(fscb);
2645         gtk_object_set_data(GTK_OBJECT(tbl), "open_checkbox", fscb);
2646         gtk_container_add(GTK_CONTAINER(hb), fscb);
2647         g_signal_connect(G_OBJECT(fscb), "toggled", GTK_SIGNAL_FUNC(sp_arctb_open_state_changed ), tbl);
2648         gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
2649     }
2651     /* Make Whole */
2652     {
2653         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
2654         GtkWidget *b = gtk_button_new_with_label(_("Make whole"));
2655         gtk_object_set_data(GTK_OBJECT(tbl), "make_whole", b);
2656         gtk_tooltips_set_tip(tt, b, _("Make the shape a whole ellipse, not arc or segment"), NULL);
2657         gtk_widget_show(b);
2658         gtk_container_add(GTK_CONTAINER(hb), b);
2659         gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_arctb_defaults), tbl);
2660         gtk_box_pack_start(GTK_BOX(tbl),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
2661     }
2663     g_object_set_data(G_OBJECT(tbl), "single", GINT_TO_POINTER(TRUE));
2664     // sensitivize make whole and open checkbox
2665     {
2666         GtkAdjustment *adj1 = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "start"));
2667         GtkAdjustment *adj2 = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "end"));
2668         sp_arctb_sensitivize (tbl, adj1->value, adj2->value);
2669     }
2671     sigc::connection *connection = new sigc::connection(
2672         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GtkObject *)tbl))
2673         );
2674     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection), connection);
2676     Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of new ellipses"));
2677     swatch->setDesktop (desktop);
2678     swatch->setClickVerb (SP_VERB_CONTEXT_ARC_PREFS);
2679     swatch->setWatchedTool ("tools.shapes.arc", true);
2680     GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
2681     gtk_box_pack_end(GTK_BOX(tbl), swatch_, FALSE, FALSE, 0);
2683     gtk_widget_show_all(tbl);
2684     sp_set_font_size_smaller (tbl);
2686     return tbl;
2692 // toggle button callbacks and updaters
2694 //########################
2695 //##      Dropper       ##
2696 //########################
2698 static void toggle_dropper_pick_alpha (GtkWidget *button, gpointer tbl) {
2699     prefs_set_int_attribute ("tools.dropper", "pick",
2700         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
2701     GtkWidget *set_checkbox = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "set_checkbox");
2702     if (set_checkbox) {
2703         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
2704             gtk_widget_set_sensitive (GTK_WIDGET (set_checkbox), TRUE);
2705         } else {
2706             gtk_widget_set_sensitive (GTK_WIDGET (set_checkbox), FALSE);
2707         }
2708     }
2709     spinbutton_defocus(GTK_OBJECT(tbl));
2712 static void toggle_dropper_set_alpha (GtkWidget *button, gpointer tbl) {
2713     prefs_set_int_attribute ("tools.dropper", "setalpha",
2714         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 1 : 0);
2715     spinbutton_defocus(GTK_OBJECT(tbl));
2719 /**
2720  * Copy the current saved desktop color to the clipboard as full hex + alpha
2721  * color representation. This is useful for passing values between various
2722  * input boxes, or directly to xml.
2723  */
2724 /* static void
2725 sp_dropper_copy( GtkWidget *widget, GtkObject *obj)
2727     GtkWidget *tbl = GTK_WIDGET(obj);
2729     SPDesktop *desktop =
2730         (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
2733     sp_dropper_c32_color_copy( sp_desktop_get_color(desktop, true) );
2734 }*/
2737 /**
2738  * Copies currently saved desktop color to the clipboard as a hex value. This
2739  * is useful for editing webpages and needing a value quickly for web
2740  * colors.
2741  *
2742  * TODO: When the toggle of the dropper is set to not mix color against
2743  *       page background, this still just gets the color of the page and
2744  *       doesn't get the actual mixed against background which is needed
2745  *       for the hex value ppl. want for web pages, etc.
2746  */
2748 /* static void
2749 sp_dropper_copy_as_hex ( GtkWidget *widget, GtkObject *obj)
2751     GtkWidget *tbl = GTK_WIDGET(obj);
2753     SPDesktop *desktop =
2754         (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl), "desktop");
2756     sp_dropper_c32_color_copy_hex( sp_desktop_get_color(desktop, true) );
2757 }*/
2760 /**
2761  * Sets the input boxes with the changed color and opacity. This is used as a
2762  * callback for style changing.
2763  */
2764 /* static bool
2765 sp_style_changed (const SPCSSAttr *css, gpointer data)
2767     // GrDrag *drag = (GrDrag *) data;
2769     // set fill of text entry box
2770     if (css->attribute("fill"))
2771         gtk_entry_set_text((GtkEntry *)dropper_rgb_entry,
2772             css->attribute("fill"));
2774     // set opacity of text entry box
2775     if (css->attribute("fill-opacity"))
2776         gtk_entry_set_text((GtkEntry *)dropper_opacity_entry,
2777             css->attribute("fill-opacity"));
2779     // set fill of text entry box
2780     if (css->attribute("stroke"))
2781         gtk_entry_set_text((GtkEntry *)dropper_rgb_entry,
2782             css->attribute("stroke"));
2784     // set opacity of text entry box
2785     if (css->attribute("stroke-opacity"))
2786         gtk_entry_set_text((GtkEntry *)dropper_opacity_entry,
2787             css->attribute("stroke-opacity"));
2788     return false;
2791 */
2794 /**
2795  * Dropper auxiliary toolbar construction and setup.
2796  *
2797  * TODO: Would like to add swatch of current color.
2798  * TODO: Add queue of last 5 or so colors selected with new swatches so that
2799  *       can drag and drop places. Will provide a nice mixing palette.
2800  */
2801 static GtkWidget *
2802 sp_dropper_toolbox_new(SPDesktop *desktop)
2804     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
2806     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2807     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2809     GtkTooltips *tt = gtk_tooltips_new();
2812     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE,
2813                        AUX_BETWEEN_BUTTON_GROUPS);
2814     // sp_toolbox_add_label(tbl, _("<b>New:</b>"));
2818     /* RGB Input Field */
2819  /*   {
2820         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
2821         GtkWidget *dropper_rgba_label = gtk_label_new ("Color:");
2822         gtk_widget_show (dropper_rgba_label);
2823         gtk_container_add(GTK_CONTAINER(hb), dropper_rgba_label);
2825         dropper_rgb_entry = gtk_entry_new ();
2826         sp_dialog_defocus_on_enter (dropper_rgb_entry);
2827         gtk_entry_set_max_length (GTK_ENTRY (dropper_rgb_entry), 7);
2828         gtk_entry_set_width_chars (GTK_ENTRY (dropper_rgb_entry), 7);
2829         gtk_tooltips_set_tip(tt, dropper_rgb_entry,
2830                          _("Hexidecimal representation of last selected "
2831                            "color"),
2832                          NULL);
2833         gtk_widget_show (dropper_rgb_entry);
2834         gtk_container_add(GTK_CONTAINER(hb), dropper_rgb_entry);
2836         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
2837                            AUX_BETWEEN_BUTTON_GROUPS);
2838     } */
2840     /* Opacity Input Field */
2841 /*    {
2842         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
2843         GtkWidget *dropper_opacity_label = gtk_label_new ( _("Opacity:") );
2844         gtk_widget_show (dropper_opacity_label);
2845         gtk_container_add(GTK_CONTAINER(hb), dropper_opacity_label);
2847         dropper_opacity_entry = gtk_entry_new ();
2848         sp_dialog_defocus_on_enter (dropper_opacity_entry);
2849         gtk_entry_set_max_length (GTK_ENTRY (dropper_opacity_entry), 11);
2850         gtk_entry_set_width_chars (GTK_ENTRY (dropper_opacity_entry), 11);
2851         gtk_tooltips_set_tip(tt, dropper_opacity_entry,
2852                          _("Opacity of last selected color"),
2853                          NULL);
2854         gtk_widget_show (dropper_opacity_entry);
2855         gtk_container_add(GTK_CONTAINER(hb), dropper_opacity_entry);
2857         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
2858                            AUX_BETWEEN_BUTTON_GROUPS);
2859     } */
2862     /* Copy to Clipboard */
2863 /*    {
2864         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
2865         GtkWidget *b = gtk_button_new_with_label(_("Copy as RGBA"));
2866         gtk_tooltips_set_tip(tt, b, _("Copy last saved color as hexidecimal "
2867                                       "RGB + Alpha (RGBA) to "
2868                                       "clipboard"),
2869                              NULL);
2870         gtk_widget_show(b);
2871         gtk_container_add(GTK_CONTAINER(hb), b);
2872         gtk_signal_connect(GTK_OBJECT(b), "clicked",
2873             GTK_SIGNAL_FUNC(sp_dropper_copy), tbl);
2874         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
2875                            AUX_BETWEEN_BUTTON_GROUPS);
2876     } */
2879     /* Copy to Clipboard as HEX */
2880 /*    {
2881         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
2882         GtkWidget *b = gtk_button_new_with_label(_("Copy as HEX"));
2883         gtk_tooltips_set_tip(tt, b, _("Copy last saved color as "
2884                                       "hexidecimal RGB without alpha "
2885                                       "to clipboard"), NULL);
2886         gtk_widget_show(b);
2887         gtk_container_add(GTK_CONTAINER(hb), b);
2888         gtk_signal_connect(GTK_OBJECT(b), "clicked",
2889             GTK_SIGNAL_FUNC(sp_dropper_copy_as_hex), tbl);
2890         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
2891                            AUX_BETWEEN_BUTTON_GROUPS);
2892     } */
2894     // aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
2896     {
2897         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
2899         GtkWidget *button = gtk_check_button_new_with_label(_("Pick alpha"));
2900         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);
2901         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
2902                                       prefs_get_int_attribute ("tools.dropper",
2903                                                                "pick", 1));
2904         gtk_widget_show(button);
2905         gtk_container_add (GTK_CONTAINER (hb), button);
2906         gtk_object_set_data(GTK_OBJECT(tbl), "pick_checkbox", button);
2907         g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (toggle_dropper_pick_alpha), tbl);
2908         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
2909                    AUX_BETWEEN_BUTTON_GROUPS);
2910     }
2912     {
2913         GtkWidget *hb = gtk_hbox_new(FALSE, 1);
2915         GtkWidget *button = gtk_check_button_new_with_label(_("Set alpha"));
2916         gtk_tooltips_set_tip(tt, button, _("If alpha was picked, assign it to selection as fill or stroke transparency"), NULL);
2917         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
2918                                       prefs_get_int_attribute ("tools.dropper",
2919                                                                "setalpha", 1));
2920         gtk_widget_show(button);
2921         gtk_container_add (GTK_CONTAINER (hb), button);
2922         gtk_object_set_data(GTK_OBJECT(tbl), "set_checkbox", button);
2923         g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (toggle_dropper_set_alpha), tbl);
2925         // make sure it's disabled if we're not picking alpha
2926         GtkWidget *pick_checkbox = (GtkWidget*) g_object_get_data(G_OBJECT(tbl), "pick_checkbox");
2927         if (pick_checkbox) {
2928             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pick_checkbox))) {
2929                 gtk_widget_set_sensitive (GTK_WIDGET (button), TRUE);
2930             } else {
2931                 gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
2932             }
2933         }
2935         gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE,
2936                    AUX_BETWEEN_BUTTON_GROUPS);
2937     }
2939     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
2942     // where new gtkmm stuff should go
2944     gtk_widget_show_all(tbl);
2945     sp_set_font_size_smaller (tbl);
2947     /*
2948     sigc::connection *connection = new sigc::connection(
2949         desktop->connectSetStyle(
2950             sigc::bind(sigc::ptr_fun(sp_style_changed),
2951                        desktop)) );
2953     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_connection),
2954                      connection); */
2956     return tbl;
2960 //########################
2961 //##    Text Toolbox    ##
2962 //########################
2963 /*
2964 static void
2965 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
2967     //Call back for letter sizing spinbutton
2970 static void
2971 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
2973     //Call back for line height spinbutton
2976 static void
2977 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
2979     //Call back for horizontal kerning spinbutton
2982 static void
2983 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
2985     //Call back for vertical kerning spinbutton
2988 static void
2989 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
2991     //Call back for letter rotation spinbutton
2992 }*/
2994 namespace {
2996 bool visible = false;
2998 void
2999 sp_text_toolbox_selection_changed (Inkscape::Selection *selection, GObject *tbl)
3002     SPStyle *query =
3003         sp_style_new ();
3005     int result_family =
3006         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3008     int result_style =
3009         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3011     int result_numbers =
3012         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3014     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3016     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3017     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3018     {
3019         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3021         if (repr)
3022         {
3023             sp_style_read_from_repr (query, repr);
3024         }
3025         else
3026         {
3027             return;
3028         }
3029     }
3031     if (query->text)
3032     {
3033         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3034             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3035             gtk_entry_set_text (GTK_ENTRY (entry), "");
3037         } else if (query->text->font_family.value) {
3039             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3040             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3042             Gtk::TreePath path;
3043             try {
3044                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3045             } catch (...) {
3046                 return;
3047             }
3049             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3050             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3052             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3054             gtk_tree_selection_select_path (tselection, path.gobj());
3055             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3057             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3058         }
3060         //Size
3061         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3062         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3063         g_object_set_data (tbl, "size-block", gpointer(1));
3064         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3065         g_object_set_data (tbl, "size-block", gpointer(0));
3066         free (str);
3068         //Anchor
3069         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3070         {
3071             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3072             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3073             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3074             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3075         }
3076         else
3077         {
3078             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3079             {
3080                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3081                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3082                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3083                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3084             }
3085             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3086             {
3087                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3088                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3089                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3090                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3091             }
3092             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3093             {
3094                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3095                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3096                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3097                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3098             }
3099         }
3101         //Style
3102         {
3103             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3105             gboolean active = gtk_toggle_button_get_active (button);
3106             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3108             if (active != check)
3109             {
3110                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3111                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3112                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3113             }
3114         }
3116         {
3117             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3119             gboolean active = gtk_toggle_button_get_active (button);
3120             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3122             if (active != check)
3123             {
3124                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3125                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3126                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3127             }
3128         }
3130         //Orientation
3131         //locking both buttons, changing one affect all group (both)
3132         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3133         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3135         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3136         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3138         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3139         {
3140             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3141         }
3142         else
3143         {
3144             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3145         }
3146         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3147         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3148     }
3151 void
3152 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint flags, GObject *tbl)
3154     sp_text_toolbox_selection_changed (selection, tbl);
3157 void
3158 sp_text_toolbox_subselection_changed (gpointer dragger, GObject *tbl)
3160     sp_text_toolbox_selection_changed (NULL, tbl);
3163 void
3164 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
3165                                 GObject             *tbl)
3167     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
3168     GtkTreeModel *model = 0;
3169     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3170     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3171     GtkTreeIter   iter;
3172     char         *family = 0;
3174     gdk_pointer_ungrab (GDK_CURRENT_TIME);
3175     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3177     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
3178         return;
3179     }
3181     gtk_tree_model_get (model, &iter, 0, &family, -1);
3183     if (g_object_get_data (G_OBJECT (selection), "block"))
3184     {
3185         gtk_entry_set_text (GTK_ENTRY (entry), family);
3186         return;
3187     }
3189     gtk_widget_hide (popdown);
3190     visible = false;
3192     gtk_entry_set_text (GTK_ENTRY (entry), family);
3194     SPStyle *query =
3195         sp_style_new ();
3197     int result_numbers =
3198         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3200     SPCSSAttr *css = sp_repr_css_attr_new ();
3201     sp_repr_css_set_property (css, "font-family", family);
3203     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3204     if (result_numbers == QUERY_STYLE_NOTHING)
3205     {
3206         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3207         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
3208     }
3209     else
3210     {
3211         sp_desktop_set_style (desktop, css, true, true);
3212     }
3214     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3215                                    _("Text: Change font family"));
3216     sp_repr_css_attr_unref (css);
3217     free (family);
3218     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3220     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3223 void
3224 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
3225                                        GObject      *tbl)
3227     const char *family = gtk_entry_get_text (entry);
3229     try {
3230         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
3231         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3232         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3233         gtk_tree_selection_select_path (selection, path.gobj());
3234         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3235         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3236     } catch (...) {
3237         if (family && strlen (family))
3238         {
3239             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3240         }
3241     }
3244 void
3245 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
3246                                    gpointer          data)
3248     if (g_object_get_data (G_OBJECT (button), "block")) return;
3249     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
3250     int prop = GPOINTER_TO_INT(data);
3252     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3253     SPCSSAttr *css = sp_repr_css_attr_new ();
3255     switch (prop)
3256     {
3257         case 0:
3258         {
3259             sp_repr_css_set_property (css, "text-anchor", "start");
3260             sp_repr_css_set_property (css, "text-align", "start");
3261             break;
3262         }
3263         case 1:
3264         {
3265             sp_repr_css_set_property (css, "text-anchor", "middle");
3266             sp_repr_css_set_property (css, "text-align", "center");
3267             break;
3268         }
3270         case 2:
3271         {
3272             sp_repr_css_set_property (css, "text-anchor", "end");
3273             sp_repr_css_set_property (css, "text-align", "end");
3274             break;
3275         }
3277         case 3:
3278         {
3279             sp_repr_css_set_property (css, "text-anchor", "start");
3280             sp_repr_css_set_property (css, "text-align", "justify");
3281             break;
3282         }
3283     }
3285     SPStyle *query =
3286         sp_style_new ();
3287     int result_numbers =
3288         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3290     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3291     if (result_numbers == QUERY_STYLE_NOTHING)
3292     {
3293         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3294     }
3296     sp_desktop_set_style (desktop, css, true, true);
3297     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3298                                    _("Text: Change alignment"));
3299     sp_repr_css_attr_unref (css);
3301     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3304 void
3305 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
3306                                gpointer          data)
3308     if (g_object_get_data (G_OBJECT (button), "block")) return;
3310     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3311     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3312     int          prop       = GPOINTER_TO_INT(data);
3313     bool         active     = gtk_toggle_button_get_active (button);
3316     switch (prop)
3317     {
3318         case 0:
3319         {
3320             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
3321             break;
3322         }
3324         case 1:
3325         {
3326             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
3327             break;
3328         }
3329     }
3331     SPStyle *query =
3332         sp_style_new ();
3333     int result_numbers =
3334         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3336     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3337     if (result_numbers == QUERY_STYLE_NOTHING)
3338     {
3339         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3340     }
3342     sp_desktop_set_style (desktop, css, true, true);
3343     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3344                                    _("Text: Change font style"));
3345     sp_repr_css_attr_unref (css);
3347     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3350 void
3351 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
3352                                      gpointer         data)
3354     if (g_object_get_data (G_OBJECT (button), "block")) {
3355         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3356         return;
3357     }
3359     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
3360     SPCSSAttr   *css        = sp_repr_css_attr_new ();
3361     int          prop       = GPOINTER_TO_INT(data);
3363     switch (prop)
3364     {
3365         case 0:
3366         {
3367             sp_repr_css_set_property (css, "writing-mode", "lr");
3368             break;
3369         }
3371         case 1:
3372         {
3373             sp_repr_css_set_property (css, "writing-mode", "tb");
3374             break;
3375         }
3376     }
3378     SPStyle *query =
3379         sp_style_new ();
3380     int result_numbers =
3381         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3383     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3384     if (result_numbers == QUERY_STYLE_NOTHING)
3385     {
3386         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3387     }
3389     sp_desktop_set_style (desktop, css, true, true);
3390     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
3391                                    _("Text: Change orientation"));
3392     sp_repr_css_attr_unref (css);
3394     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3397 gboolean
3398 sp_text_toolbox_size_keypress (GtkWidget *w, GdkEventKey *event, gpointer data)
3400     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3401     if (!desktop) return FALSE;
3403     switch (get_group0_keyval (event)) {
3404         case GDK_Escape: // defocus
3405             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3406             return TRUE; // I consumed the event
3407             break;
3408         case GDK_Return: // defocus
3409         case GDK_KP_Enter:
3410             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3411             return TRUE; // I consumed the event
3412             break;
3413     }
3414     return FALSE;
3417 gboolean
3418 sp_text_toolbox_family_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3420     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3421     if (!desktop) return FALSE;
3423     switch (get_group0_keyval (event)) {
3424         case GDK_Escape: // defocus
3425             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3426             sp_text_toolbox_selection_changed (NULL, tbl); // update
3427             return TRUE; // I consumed the event
3428             break;
3429     }
3430     return FALSE;
3433 gboolean
3434 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject *tbl)
3436     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3437     if (!desktop) return FALSE;
3439     switch (get_group0_keyval (event)) {
3440         case GDK_Escape: // defocus
3441             gtk_widget_hide (w);
3442             visible = false;
3443             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3444             return TRUE; // I consumed the event
3445             break;
3446     }
3447     return FALSE;
3451 void
3452 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
3453                                GObject     *tbl)
3455     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3457     if (g_object_get_data (tbl, "size-block")) return;
3459 #if GTK_CHECK_VERSION(2,6,0)
3460     char *text = gtk_combo_box_get_active_text (cbox);
3461 #else // GTK_CHECK_VERSION(2,6,0)
3462     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
3463     GtkTreeIter iter;
3464     char *text = NULL;
3466     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
3467         gtk_tree_model_get (model, &iter, 0, &text, -1);
3468 #endif // GTK_CHECK_VERSION(2,6,0)
3470     SPCSSAttr *css = sp_repr_css_attr_new ();
3471     sp_repr_css_set_property (css, "font-size", text);
3472     free (text);
3474     SPStyle *query =
3475         sp_style_new ();
3476     int result_numbers =
3477         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3479     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3480     if (result_numbers == QUERY_STYLE_NOTHING)
3481     {
3482         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
3483     }
3485     sp_desktop_set_style (desktop, css, true, true);
3486     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
3487                                    _("Text: Change font size"));
3488     sp_repr_css_attr_unref (css);
3491     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
3492         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3495 void
3496 sp_text_toolbox_text_popdown_clicked    (GtkButton          *button,
3497                                          GObject            *tbl)
3499     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
3500     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
3501     int x, y;
3503     if (!visible)
3504     {
3505         gdk_window_get_origin (widget->window, &x, &y);
3506         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
3507         gtk_widget_show_all (popdown);
3509         gdk_pointer_grab (widget->window, TRUE,
3510                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
3511                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
3512                                         GDK_POINTER_MOTION_MASK),
3513                           NULL, NULL, GDK_CURRENT_TIME);
3515         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
3517         visible = true;
3518     }
3519     else
3520     {
3521         gdk_pointer_ungrab (GDK_CURRENT_TIME);
3522         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
3523         gtk_widget_hide (popdown);
3524         visible = false;
3525     }
3528 gboolean
3529 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
3530                                  GdkEventFocus    *event,
3531                                  GObject          *tbl)
3533     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
3534     return FALSE;
3537 gboolean
3538 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
3539                                    GdkEventFocus    *event,
3540                                    GObject          *tbl)
3542     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
3544     gtk_widget_hide (popdown);
3545     visible = false;
3546     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
3547     return TRUE;
3550 void
3551 cell_data_func  (GtkTreeViewColumn *column,
3552                  GtkCellRenderer   *cell,
3553                  GtkTreeModel      *tree_model,
3554                  GtkTreeIter       *iter,
3555                  gpointer           data)
3557     char        *family,
3558         *family_escaped,
3559         *sample_escaped;
3561     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
3563     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
3565     family_escaped = g_markup_escape_text (family, -1);
3566     sample_escaped = g_markup_escape_text (sample, -1);
3568     std::stringstream markup;
3569     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
3570     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
3572     free (family);
3573     free (family_escaped);
3574     free (sample_escaped);
3577 static void delete_completion(GObject *obj, GtkWidget *entry) {
3578     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
3579     if (completion) {
3580         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
3581         g_object_unref (completion);
3582     }
3585 GtkWidget*
3586 sp_text_toolbox_new (SPDesktop *desktop)
3588     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
3590     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
3591     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
3593     GtkTooltips *tt = gtk_tooltips_new();
3594     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
3596     ////////////Family
3597     //Window
3598     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3599     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
3601     //Entry
3602     GtkWidget           *entry = gtk_entry_new ();
3603     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
3604     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
3605     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
3606     gtk_entry_completion_set_text_column (completion, 0);
3607     gtk_entry_completion_set_minimum_key_length (completion, 1);
3608     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
3609     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
3610     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
3611     aux_toolbox_space (tbl, 1);
3612     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
3613     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
3615     //Button
3616     GtkWidget   *button = gtk_button_new ();
3617     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
3618     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
3620     //Popdown
3621     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
3622     GtkWidget           *treeview = gtk_tree_view_new ();
3624     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
3625     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
3626     gtk_tree_view_column_pack_start (column, cell, FALSE);
3627     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
3628     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
3629     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
3631     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
3632     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
3633 #if GTK_CHECK_VERSION(2,6,0)
3634     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
3635 #endif // GTK_CHECK_VERSION(2,6,0)
3637     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
3639     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
3640     gtk_container_add (GTK_CONTAINER (sw), treeview);
3642     gtk_container_add (GTK_CONTAINER (window), sw);
3643     gtk_widget_set_size_request (window, 300, 450);
3645     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
3646     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
3647     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
3649     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
3651     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
3652     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
3654     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
3655     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
3657     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
3658     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
3659     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
3660     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
3661     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
3663     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
3664     aux_toolbox_space (tbl, 1);
3665     GtkWidget *box = gtk_event_box_new ();
3666     gtk_container_add (GTK_CONTAINER (box), image);
3667     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
3668     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
3669     GtkTooltips *tooltips = gtk_tooltips_new ();
3670     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
3671     gtk_widget_hide (GTK_WIDGET (box));
3672     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
3674     ////////////Size
3675     const char *sizes[] = {
3676         "4", "6", "8", "9", "10", "11", "12", "13", "14",
3677         "16", "18", "20", "22", "24", "28",
3678         "32", "36", "40", "48", "56", "64", "72", "144"
3679     };
3681     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
3682     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
3683     gtk_widget_set_size_request (cbox, 80, -1);
3684     aux_toolbox_space (tbl, 1);
3685     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
3686     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
3687     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
3688     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
3690     //spacer
3691     aux_toolbox_space (tbl, 4);
3692     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
3694     ////////////Text anchor
3695     GtkWidget *group   = gtk_radio_button_new (NULL);
3696     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
3697     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
3699     // left
3700     GtkWidget *rbutton = group;
3701     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3702     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
3703     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3705     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3706     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
3707     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
3708     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
3710     // center
3711     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3712     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3713     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
3714     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3716     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3717     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
3718     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
3719     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
3721     // right
3722     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3723     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3724     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
3725     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3727     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3728     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
3729     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
3730     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
3732     // fill
3733     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3734     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3735     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
3736     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3738     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3739     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
3740     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
3741     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
3743     aux_toolbox_space (tbl, 1);
3744     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
3746     //spacer
3747     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
3749     ////////////Text style
3750     row = gtk_hbox_new (FALSE, 4);
3752     // bold
3753     rbutton = gtk_toggle_button_new ();
3754     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3755     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
3756     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3757     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
3759     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3760     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
3761     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
3763     // italic
3764     rbutton = gtk_toggle_button_new ();
3765     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3766     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
3767     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3768     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
3770     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3771     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
3772     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
3774     aux_toolbox_space (tbl, 1);
3775     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
3777     //spacer
3778     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
3780     ////////////Text orientation
3781     group   = gtk_radio_button_new (NULL);
3782     row     = gtk_hbox_new (FALSE, 4);
3783     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
3785     // horizontal
3786     rbutton = group;
3787     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3788     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
3789     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3790     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
3792     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3793     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
3794     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
3796     // vertical
3797     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3798     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
3799     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
3800     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
3801     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
3803     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
3804     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
3805     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
3806     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
3809     //watch selection
3810     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
3812     sigc::connection *c_selection_changed =
3813         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
3814                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
3815     pool->add_connection ("selection-changed", c_selection_changed);
3817     sigc::connection *c_selection_modified =
3818         new sigc::connection (sp_desktop_selection (desktop)->connectModified
3819                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
3820     pool->add_connection ("selection-modified", c_selection_modified);
3822     sigc::connection *c_subselection_changed =
3823         new sigc::connection (desktop->connectToolSubselectionChanged
3824                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
3825     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
3827     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
3830 #if 0
3831     // horizontal
3832     {
3833         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
3834         GtkWidget *b = group = gtk_radio_button_new (NULL);
3835         gtk_container_add (GTK_CONTAINER (b), px);
3836         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
3837         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
3838         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
3839         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
3840     }
3842     // vertical
3843     {
3844         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
3845         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
3846         gtk_container_add (GTK_CONTAINER (b), px);
3847         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
3848         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
3849         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
3850         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
3851     }
3853     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
3855     // letter spacing
3856     {
3857         {
3858             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
3859             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3860             gtk_container_add (GTK_CONTAINER (hb), image);
3861             gtk_widget_show(image);
3862             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3863         }
3865         {
3866             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between letters"),
3867                                              "tools.text", "letter_spacing", 0.0,
3868                                              us, tbl, FALSE, NULL,
3869                                              -1000.0, 1000.0, 0.1, 0.1,
3870                                              sp_text_letter_changed, 0.1, 1);
3871             gtk_widget_set_size_request (hb, 45, 6);
3872             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
3873         }
3874     }
3876     // line spacing
3877     {
3878         {
3879             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
3880             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3881             gtk_container_add (GTK_CONTAINER (hb), image);
3882             gtk_widget_show(image);
3883             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3884         }
3886         {
3887             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Spacing between lines"),
3888                                              "tools.text", "line_spacing", 0,
3889                                              us, tbl, FALSE, NULL,
3890                                              -1000.0, 1000.0, 0.1, 0.1,
3891                                              sp_text_line_changed, 0.1, 1);
3892             gtk_widget_set_size_request (hb, 45, 0);
3893             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
3894         }
3895     }
3897     {
3898         // horizontal kerning/vertical kerning units menu: create
3899         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
3900         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
3901         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
3903         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
3905         // horizontal kerning
3906         {
3907             {
3908                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
3909                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3910                 gtk_container_add (GTK_CONTAINER (hb), image);
3911                 gtk_widget_show(image);
3912                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3913             }
3915             {
3916                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Horizontal kerning"),
3917                                                  "tools.text", "horizontal_kerning", 0,
3918                                                  us, tbl, FALSE, NULL,
3919                                                  -100.00, 100.00, 0.01, 0.1,
3920                                                  sp_text_horiz_kern_changed);
3921                 gtk_widget_set_size_request (hb, 45, 0);
3922                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
3923             }
3924         }
3926         // vertical kerning
3927         {
3928             {
3929                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
3930                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3931                 gtk_container_add (GTK_CONTAINER (hb), image);
3932                 gtk_widget_show(image);
3933                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3934             }
3936             {
3937                 GtkWidget *hb = sp_tb_spinbutton(_(""), _("Vertical kerning"),
3938                                                  "tools.text", "vertical_kerning", 0,
3939                                                  us, tbl, FALSE, NULL,
3940                                                  -100.00, 100.00, 0.01, 0.1,
3941                                                  sp_text_vert_kern_changed);
3942                 gtk_widget_set_size_request (hb, 45, 0);
3943                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
3944             }
3945         }
3947         // add the units menu
3948         gtk_widget_show(us);
3949         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
3950         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
3951     }
3953     // letter rotation
3954     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
3955     {
3956         {
3957             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
3958             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
3959             gtk_container_add (GTK_CONTAINER (hb), image);
3960             gtk_widget_show(image);
3961             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
3962         }
3963         {
3964             GtkWidget *hb = sp_tb_spinbutton(_(""), _("Letter rotation"),
3965                                              "tools.text", "letter_rotation", 0,
3966                                              us, tbl, FALSE, NULL,
3967                                              -180.0, 180.0, 0.1, 0.1,
3968                                              sp_text_letter_rotation_changed, 0.1, 1);
3969             gtk_widget_set_size_request (hb, 45, 0);
3970             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
3971         }
3972         // rotation degree label
3973         {
3974             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
3975             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
3976         }
3977     }
3979     // Remove Manual Kerns
3980     {
3981         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
3982         GtkWidget *button = gtk_button_new ();
3983         gtk_container_add (GTK_CONTAINER (button), px);
3984         gtk_widget_show(button);
3985         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
3986         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
3987         gtk_widget_set_sensitive(button, TRUE);
3988         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
3989     }
3990 #endif
3992     gtk_widget_show_all (tbl);
3993     return tbl;
3995 } // end of sp_text_toolbox_new()
3997 }//<unnamed> namespace
4000 //#########################
4001 //##  Connector Toolbox  ##
4002 //#########################
4004 static void sp_connector_path_set_avoid(void)
4006     cc_selection_set_avoid(true);
4010 static void sp_connector_path_set_ignore(void)
4012     cc_selection_set_avoid(false);
4017 static void connector_spacing_changed(GtkAdjustment *adj, GtkWidget *tbl)
4019     // quit if run by the _changed callbacks
4020     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4021         return;
4022     }
4024     SPDesktop *desktop = (SPDesktop *) gtk_object_get_data(GTK_OBJECT(tbl),
4025             "desktop");
4026     SPDocument *doc = sp_desktop_document(desktop);
4028     if (!sp_document_get_undo_sensitive(doc))
4029     {
4030         return;
4031     }
4033     // in turn, prevent callbacks from responding
4034     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
4036     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4038     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4039     SP_OBJECT(desktop->namedview)->updateRepr();
4041     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4042     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4043         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4044         NR::Matrix m = NR::identity();
4045         avoid_item_move(&m, item);
4046     }
4048     if (items) {
4049         g_slist_free(items);
4050     }
4052     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4053             _("Change connector spacing"));
4055     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
4057     spinbutton_defocus(GTK_OBJECT(tbl));
4060 static void sp_connector_graph_layout(void)
4062     if (!SP_ACTIVE_DESKTOP) return;
4064     // hack for clones, see comment in align-and-distribute.cpp
4065     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4066     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4068     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4070     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4072     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4075 static void
4076 sp_directed_graph_layout_toggled(GtkWidget *widget, GtkObject *tbl)
4078     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
4079         prefs_set_string_attribute("tools.connector", "directedlayout",
4080                 "true");
4081     } else {
4082         prefs_set_string_attribute("tools.connector", "directedlayout",
4083                 "false");
4084     }
4086 static void
4087 sp_nooverlaps_graph_layout_toggled(GtkWidget *widget, GtkObject *tbl)
4089     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
4090         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4091                 "true");
4092     } else {
4093         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4094                 "false");
4095     }
4099 static void connector_length_changed(GtkAdjustment *adj, GtkWidget *tbl)
4101     prefs_set_double_attribute("tools.connector", "length", adj->value);
4102     spinbutton_defocus(GTK_OBJECT(tbl));
4105 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4106         gchar const *name, gchar const *old_value, gchar const *new_value,
4107         bool is_interactive, gpointer data)
4109     GtkWidget *tbl = GTK_WIDGET(data);
4111     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4112         return;
4113     }
4114     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4115         return;
4116     }
4118     GtkAdjustment *adj = (GtkAdjustment*)
4119             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4120     gdouble spacing = defaultConnSpacing;
4121     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4123     gtk_adjustment_set_value(adj, spacing);
4127 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4128     NULL, /* child_added */
4129     NULL, /* child_removed */
4130     connector_tb_event_attr_changed,
4131     NULL, /* content_changed */
4132     NULL  /* order_changed */
4133 };
4136 static GtkWidget *
4137 sp_connector_toolbox_new(SPDesktop *desktop)
4139     GtkTooltips *tt = gtk_tooltips_new();
4140     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
4142     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4143     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4145     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE,
4146             AUX_BETWEEN_BUTTON_GROUPS);
4148     sp_toolbox_button_new(tbl, Inkscape::ICON_SIZE_SMALL_TOOLBAR,
4149             "connector_avoid", GTK_SIGNAL_FUNC(sp_connector_path_set_avoid),
4150             tt, _("Make connectors avoid selected objects"));
4152     sp_toolbox_button_new(tbl, Inkscape::ICON_SIZE_SMALL_TOOLBAR,
4153             "connector_ignore", GTK_SIGNAL_FUNC(sp_connector_path_set_ignore),
4154             tt, _("Make connectors ignore selected objects"));
4156     //  interval
4157     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE,
4158             AUX_BETWEEN_BUTTON_GROUPS);
4160     // Spacing spinbox
4161     {
4162         GtkWidget *object_spacing = sp_tb_spinbutton(_("Spacing:"),
4163                 _("The amount of space left around objects by auto-routing connectors"),
4164                 "tools.connector", "spacing", 10, NULL, tbl, TRUE,
4165                 "inkscape:connector-spacing", 0, 100, 1.0, 10.0,
4166                 connector_spacing_changed, 1, 0);
4168         gtk_box_pack_start(GTK_BOX(tbl), object_spacing, FALSE, FALSE,
4169                 AUX_SPACING);
4170     }
4171     // Graph (connector network) layout
4172     sp_toolbox_button_new(tbl, Inkscape::ICON_SIZE_SMALL_TOOLBAR,
4173             "graph_layout", GTK_SIGNAL_FUNC(sp_connector_graph_layout),
4174             tt, _("Nicely arrange selected connector network"));
4175     // Default connector length spinbox
4176     {
4177         GtkWidget *connector_length = sp_tb_spinbutton(_("Length:"),
4178                 _("Ideal length for connectors when layout is applied"),
4179                 "tools.connector", "length", 100, NULL, tbl, TRUE,
4180                 "inkscape:connector-length", 10, 1000, 10.0, 100.0,
4181                 connector_length_changed, 1, 0);
4183         gtk_box_pack_start(GTK_BOX(tbl), connector_length, FALSE, FALSE,
4184                 AUX_SPACING);
4185     }
4186     gchar const *tbuttonstate;
4187     // Directed edges toggle button
4188     {
4189         GtkWidget *tbutton = gtk_toggle_button_new ();
4190         gtk_button_set_relief       (GTK_BUTTON (tbutton), GTK_RELIEF_NONE);
4191         gtk_container_add           (GTK_CONTAINER (tbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, "directed_graph"));
4192         gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (tbutton), FALSE);
4193         gtk_tooltips_set_tip(tt, tbutton, _("Make connectors with end-markers (arrows) point downwards"), NULL);
4195         gtk_box_pack_start  (GTK_BOX  (tbl), tbutton, FALSE, FALSE, 0);
4196         g_signal_connect(G_OBJECT(tbutton), "toggled", GTK_SIGNAL_FUNC(sp_directed_graph_layout_toggled), tbl);
4197         tbuttonstate = prefs_get_string_attribute("tools.connector", "directedlayout");
4198         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(tbutton),
4199                 (tbuttonstate && !strcmp(tbuttonstate, "true"))?TRUE:FALSE );
4200     }
4201     // Avoid overlaps toggle button
4202     {
4203         GtkWidget *tbutton = gtk_toggle_button_new ();
4204         gtk_button_set_relief       (GTK_BUTTON (tbutton), GTK_RELIEF_NONE);
4205         gtk_container_add           (GTK_CONTAINER (tbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, "remove_overlaps"));
4206         gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (tbutton), FALSE);
4207         gtk_tooltips_set_tip(tt, tbutton, _("Do not allow overlapping shapes"), NULL);
4209         gtk_box_pack_start  (GTK_BOX  (tbl), tbutton, FALSE, FALSE, 0);
4210         g_signal_connect(G_OBJECT(tbutton), "toggled", GTK_SIGNAL_FUNC(sp_nooverlaps_graph_layout_toggled), tbl);
4211         tbuttonstate = prefs_get_string_attribute("tools.connector", "avoidoverlaplayout");
4212         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(tbutton),
4213                 (tbuttonstate && !strcmp(tbuttonstate, "true"))?TRUE:FALSE );
4214     }
4216     gtk_widget_show_all(tbl);
4217     sp_set_font_size_smaller (tbl);
4219     // Code to watch for changes to the connector-spacing attribute in
4220     // the XML.
4221     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4222     g_assert(repr != NULL);
4224     Inkscape::XML::Node *oldrepr = (Inkscape::XML::Node *)
4225             gtk_object_get_data(GTK_OBJECT(tbl), "repr");
4227     if (oldrepr) { // remove old listener
4228         sp_repr_remove_listener_by_data(oldrepr, tbl);
4229         Inkscape::GC::release(oldrepr);
4230         oldrepr = NULL;
4231         g_object_set_data(G_OBJECT(tbl), "repr", NULL);
4232     }
4234     if (repr) {
4235         g_object_set_data(G_OBJECT(tbl), "repr", repr);
4236         Inkscape::GC::anchor(repr);
4237         sp_repr_add_listener(repr, &connector_tb_repr_events, tbl);
4238         sp_repr_synthesize_events(repr, &connector_tb_repr_events, tbl);
4239     }
4241     return tbl;
4243 } // end of sp_connector_toolbox_new()
4245 static void paintbucket_threshold_changed(GtkAdjustment *adj, GtkWidget *tbl)
4247     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
4248     spinbutton_defocus(GTK_OBJECT(tbl));
4251 static void paintbucket_offset_changed(GtkAdjustment *adj, GtkWidget *tbl)
4253     GtkWidget *us = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(tbl), "units");
4254     SPUnit const *unit = sp_unit_selector_get_unit(SP_UNIT_SELECTOR(us));
4256     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
4257     spinbutton_defocus(GTK_OBJECT(tbl));
4260 static GtkWidget *
4261 sp_paintbucket_toolbox_new(SPDesktop *desktop)
4263     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
4265     //  interval
4266     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4268     {
4269       sp_toolbox_add_label(tbl, _("Fill by:"), false);
4271       GtkWidget *channels = gtk_combo_box_new_text();
4273       GList *items = flood_channels_dropdown_items_list();
4275       for ( ; items ; items = items->next )
4276       {
4277           gtk_combo_box_append_text(GTK_COMBO_BOX(channels), (char*)items->data);
4278       }
4280       gtk_combo_box_set_active (GTK_COMBO_BOX(channels), prefs_get_int_attribute("tools.paintbucket", "channels", 0));
4281       gtk_box_pack_start (GTK_BOX (tbl), channels, FALSE, FALSE, 0);
4282       g_signal_connect (G_OBJECT (channels), "changed", G_CALLBACK (flood_channels_changed), tbl);
4283     }
4285     //  interval
4286     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4288     // Spacing spinbox
4289     {
4290         GtkWidget *threshold = sp_tb_spinbutton(_("Threshold:"),
4291                 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
4292                 "tools.paintbucket", "threshold", 5, NULL, tbl, TRUE,
4293                 "inkscape:paintbucket-threshold", 0, 100, 1.0, 10.0,
4294                 paintbucket_threshold_changed, 1, 0);
4296         gtk_box_pack_start(GTK_BOX(tbl), threshold, FALSE, FALSE,
4297                 AUX_SPACING);
4298     }
4300     //  interval
4301     gtk_box_pack_start(GTK_BOX(tbl), gtk_hbox_new(FALSE, 0), FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4303     // Create the units menu.
4304     GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4305     sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4306     sp_unit_selector_set_unit (SP_UNIT_SELECTOR(us), sp_desktop_namedview(desktop)->doc_units);
4308     // Offset spinbox
4309     {
4310         GtkWidget *offset = sp_tb_spinbutton(_("Grow/shrink by:"),
4311                 _("The amount to grow (positive) or shrink (negative) the created fill path"),
4312                 "tools.paintbucket", "offset", 0, us, tbl, TRUE,
4313                 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
4314                 paintbucket_offset_changed, 1, 2);
4316         gtk_box_pack_start(GTK_BOX(tbl), offset, FALSE, FALSE,
4317                 AUX_SPACING);
4319         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, AUX_SPACING);
4320         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4321     }
4323     Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of Paint Bucket fill objects"));
4324     swatch->setDesktop (desktop);
4325     swatch->setClickVerb (SP_VERB_CONTEXT_PAINTBUCKET_PREFS);
4326     swatch->setWatchedTool ("tools.paintbucket", true);
4327     GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
4328     gtk_box_pack_end(GTK_BOX(tbl), swatch_, FALSE, FALSE, 0);
4330     gtk_widget_show_all(tbl);
4331     sp_set_font_size_smaller (tbl);
4332     return tbl;
4335 /*
4336   Local Variables:
4337   mode:c++
4338   c-file-style:"stroustrup"
4339   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4340   indent-tabs-mode:nil
4341   fill-column:99
4342   End:
4343 */
4344 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :