Code

further fixes to font size combo: activate on defocus except when caused by esc;...
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <gtkmm.h>
31 #include <gtk/gtk.h>
32 #include <iostream>
33 #include <sstream>
35 #include "widgets/button.h"
36 #include "widgets/widget-sizes.h"
37 #include "widgets/spw-utilities.h"
38 #include "widgets/spinbutton-events.h"
39 #include "dialogs/text-edit.h"
41 #include "ui/widget/style-swatch.h"
43 #include "prefs-utils.h"
44 #include "verbs.h"
45 #include "sp-namedview.h"
46 #include "desktop.h"
47 #include "desktop-handles.h"
48 #include "xml/repr.h"
49 #include "xml/node-event-vector.h"
50 #include <glibmm/i18n.h>
51 #include "helper/unit-menu.h"
52 #include "helper/units.h"
54 #include "inkscape.h"
55 #include "conn-avoid-ref.h"
58 #include "select-toolbar.h"
59 #include "gradient-toolbar.h"
61 #include "connector-context.h"
62 #include "node-context.h"
63 #include "shape-editor.h"
64 #include "tweak-context.h"
65 #include "sp-rect.h"
66 #include "box3d.h"
67 #include "box3d-context.h"
68 #include "sp-star.h"
69 #include "sp-spiral.h"
70 #include "sp-ellipse.h"
71 #include "sp-text.h"
72 #include "sp-flowtext.h"
73 #include "style.h"
74 #include "selection.h"
75 #include "selection-chemistry.h"
76 #include "document-private.h"
77 #include "desktop-style.h"
78 #include "../libnrtype/font-lister.h"
79 #include "../libnrtype/font-instance.h"
80 #include "../connection-pool.h"
81 #include "../prefs-utils.h"
82 #include "../inkscape-stock.h"
83 #include "icon.h"
84 #include "graphlayout/graphlayout.h"
86 #include "mod360.h"
88 #include "toolbox.h"
90 #include "flood-context.h"
92 #include "ink-action.h"
93 #include "ege-adjustment-action.h"
94 #include "ege-output-action.h"
95 #include "ege-select-one-action.h"
96 #include "helper/unit-tracker.h"
98 #include "svg/css-ostringstream.h"
100 using Inkscape::UnitTracker;
102 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
103 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
105 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
118 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
124 static struct {
125     gchar const *type_name;
126     gchar const *data_name;
127     sp_verb_t verb;
128     sp_verb_t doubleclick_verb;
129 } const tools[] = {
130     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
131     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
132     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
133     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
134     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
135     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
136     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
137     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
138     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
139     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
140     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
141     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
142     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
143     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
144     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
145     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
146     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
147     { NULL, NULL, 0, 0 }
148 };
150 static struct {
151     gchar const *type_name;
152     gchar const *data_name;
153     GtkWidget *(*create_func)(SPDesktop *desktop);
154     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
155     gchar const *ui_name;
156     gint swatch_verb_id;
157     gchar const *swatch_tool;
158     gchar const *swatch_tip;
159 } const aux_toolboxes[] = {
160     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
161       SP_VERB_INVALID, 0, 0},
162     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
163       SP_VERB_INVALID, 0, 0},
164     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
165       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
166     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
167       SP_VERB_INVALID, 0, 0},
168     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
169       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
170     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
171       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
172     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
173       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
174     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
175       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
176     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
177       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
178     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
179       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
180     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
181       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
182     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
183       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
184     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
185       SP_VERB_INVALID, 0, 0},
186     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
187       SP_VERB_INVALID, 0, 0},
188     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
189       SP_VERB_INVALID, 0, 0},
190     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
191       SP_VERB_INVALID, 0, 0},
192     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
193       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
194     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
195 };
198 static gchar const * ui_descr =
199         "<ui>"
200         "  <toolbar name='SelectToolbar'>"
201         "    <toolitem action='EditSelectAll' />"
202         "    <toolitem action='EditSelectAllInAllLayers' />"
203         "    <toolitem action='EditDeselect' />"
204         "    <separator />"
205         "    <toolitem action='ObjectRotate90CCW' />"
206         "    <toolitem action='ObjectRotate90' />"
207         "    <toolitem action='ObjectFlipHorizontally' />"
208         "    <toolitem action='ObjectFlipVertically' />"
209         "    <separator />"
210         "    <toolitem action='SelectionToBack' />"
211         "    <toolitem action='SelectionLower' />"
212         "    <toolitem action='SelectionRaise' />"
213         "    <toolitem action='SelectionToFront' />"
214         "    <separator />"
215         "    <toolitem action='XAction' />"
216         "    <toolitem action='YAction' />"
217         "    <toolitem action='WidthAction' />"
218         "    <toolitem action='LockAction' />"
219         "    <toolitem action='HeightAction' />"
220         "    <toolitem action='UnitsAction' />"
221         "    <separator />"
222         "    <toolitem action='transform_affect_label' />"
223         "    <toolitem action='transform_stroke' />"
224         "    <toolitem action='transform_corners' />"
225         "    <toolitem action='transform_gradient' />"
226         "    <toolitem action='transform_pattern' />"
227         "  </toolbar>"
229         "  <toolbar name='NodeToolbar'>"
230         "    <toolitem action='NodeInsertAction' />"
231         "    <toolitem action='NodeDeleteAction' />"
232         "    <separator />"
233         "    <toolitem action='NodeJoinAction' />"
234         "    <toolitem action='NodeJoinSegmentAction' />"
235         "    <toolitem action='NodeDeleteSegmentAction' />"
236         "    <toolitem action='NodeBreakAction' />"
237         "    <separator />"
238         "    <toolitem action='NodeCuspAction' />"
239         "    <toolitem action='NodeSmoothAction' />"
240         "    <toolitem action='NodeSymmetricAction' />"
241         "    <separator />"
242         "    <toolitem action='NodeLineAction' />"
243         "    <toolitem action='NodeCurveAction' />"
244         "    <separator />"
245         "    <toolitem action='ObjectToPath' />"
246         "    <toolitem action='StrokeToPath' />"
247         "    <separator />"
248         "    <toolitem action='NodesShowHandlesAction' />"
249         "    <separator />"
250         "    <toolitem action='EditNextLPEParameterAction' />"
251         "    <separator />"
252         "    <toolitem action='NodeXAction' />"
253         "    <toolitem action='NodeYAction' />"
254         "    <toolitem action='NodeUnitsAction' />"
255         "  </toolbar>"
257         "  <toolbar name='TweakToolbar'>"
258         "    <toolitem action='TweakWidthAction' />"
259         "    <separator />"
260         "    <toolitem action='TweakForceAction' />"
261         "    <toolitem action='TweakPressureAction' />"
262         "    <separator />"
263         "    <toolitem action='TweakModeAction' />"
264         "    <separator />"
265         "    <toolitem action='TweakFidelityAction' />"
266         "    <separator />"
267         "    <toolitem action='TweakChannelsLabel' />"
268         "    <toolitem action='TweakDoH' />"
269         "    <toolitem action='TweakDoS' />"
270         "    <toolitem action='TweakDoL' />"
271         "    <toolitem action='TweakDoO' />"
272         "  </toolbar>"
274         "  <toolbar name='ZoomToolbar'>"
275         "    <toolitem action='ZoomIn' />"
276         "    <toolitem action='ZoomOut' />"
277         "    <separator />"
278         "    <toolitem action='Zoom1:0' />"
279         "    <toolitem action='Zoom1:2' />"
280         "    <toolitem action='Zoom2:1' />"
281         "    <separator />"
282         "    <toolitem action='ZoomSelection' />"
283         "    <toolitem action='ZoomDrawing' />"
284         "    <toolitem action='ZoomPage' />"
285         "    <toolitem action='ZoomPageWidth' />"
286         "    <separator />"
287         "    <toolitem action='ZoomPrev' />"
288         "    <toolitem action='ZoomNext' />"
289         "  </toolbar>"
291         "  <toolbar name='StarToolbar'>"
292         "    <separator />"
293         "    <toolitem action='StarStateAction' />"
294         "    <separator />"
295         "    <toolitem action='FlatAction' />"
296         "    <separator />"
297         "    <toolitem action='MagnitudeAction' />"
298         "    <toolitem action='SpokeAction' />"
299         "    <toolitem action='RoundednessAction' />"
300         "    <toolitem action='RandomizationAction' />"
301         "    <separator />"
302         "    <toolitem action='StarResetAction' />"
303         "  </toolbar>"
305         "  <toolbar name='RectToolbar'>"
306         "    <toolitem action='RectStateAction' />"
307         "    <toolitem action='RectWidthAction' />"
308         "    <toolitem action='RectHeightAction' />"
309         "    <toolitem action='RadiusXAction' />"
310         "    <toolitem action='RadiusYAction' />"
311         "    <toolitem action='RectUnitsAction' />"
312         "    <separator />"
313         "    <toolitem action='RectResetAction' />"
314         "  </toolbar>"
316         "  <toolbar name='3DBoxToolbar'>"
317         "    <toolitem action='3DBoxAngleXAction' />"
318         "    <toolitem action='3DBoxVPXStateAction' />"
319         "    <separator />"
320         "    <toolitem action='3DBoxAngleYAction' />"
321         "    <toolitem action='3DBoxVPYStateAction' />"
322         "    <separator />"
323         "    <toolitem action='3DBoxAngleZAction' />"
324         "    <toolitem action='3DBoxVPZStateAction' />"
325         "  </toolbar>"
327         "  <toolbar name='SpiralToolbar'>"
328         "    <toolitem action='SpiralStateAction' />"
329         "    <toolitem action='SpiralRevolutionAction' />"
330         "    <toolitem action='SpiralExpansionAction' />"
331         "    <toolitem action='SpiralT0Action' />"
332         "    <separator />"
333         "    <toolitem action='SpiralResetAction' />"
334         "  </toolbar>"
336         "  <toolbar name='PenToolbar'>"
337         "  </toolbar>"
339         "  <toolbar name='PencilToolbar'>"
340         "  </toolbar>"
342         "  <toolbar name='CalligraphyToolbar'>"
343         "    <separator />"
344         "    <toolitem action='CalligraphyWidthAction' />"
345         "    <toolitem action='PressureAction' />"
346         "    <toolitem action='TraceAction' />"
347         "    <toolitem action='ThinningAction' />"
348         "    <separator />"
349         "    <toolitem action='AngleAction' />"
350         "    <toolitem action='TiltAction' />"
351         "    <toolitem action='FixationAction' />"
352         "    <separator />"
353         "    <toolitem action='CapRoundingAction' />"
354         "    <separator />"
355         "    <toolitem action='TremorAction' />"
356         "    <toolitem action='WiggleAction' />"
357         "    <toolitem action='MassAction' />"
358         "    <separator />"
359         "    <toolitem action='CalligraphyResetAction' />"
360         "  </toolbar>"
362         "  <toolbar name='ArcToolbar'>"
363         "    <toolitem action='ArcStateAction' />"
364         "    <separator />"
365         "    <toolitem action='ArcStartAction' />"
366         "    <toolitem action='ArcEndAction' />"
367         "    <separator />"
368         "    <toolitem action='ArcOpenAction' />"
369         "    <separator />"
370         "    <toolitem action='ArcResetAction' />"
371         "    <separator />"
372         "  </toolbar>"
374         "  <toolbar name='PaintbucketToolbar'>"
375         "    <toolitem action='ChannelsAction' />"
376         "    <separator />"
377         "    <toolitem action='ThresholdAction' />"
378         "    <separator />"
379         "    <toolitem action='OffsetAction' />"
380         "    <toolitem action='PaintbucketUnitsAction' />"
381         "    <separator />"
382         "    <toolitem action='AutoGapAction' />"
383         "    <separator />"
384         "    <toolitem action='PaintbucketResetAction' />"
385         "  </toolbar>"
387         "  <toolbar name='DropperToolbar'>"
388         "    <toolitem action='DropperPickAlphaAction' />"
389         "    <toolitem action='DropperSetAlphaAction' />"
390         "  </toolbar>"
392         "  <toolbar name='ConnectorToolbar'>"
393         "    <toolitem action='ConnectorAvoidAction' />"
394         "    <toolitem action='ConnectorIgnoreAction' />"
395         "    <toolitem action='ConnectorSpacingAction' />"
396         "    <toolitem action='ConnectorGraphAction' />"
397         "    <toolitem action='ConnectorLengthAction' />"
398         "    <toolitem action='ConnectorDirectedAction' />"
399         "    <toolitem action='ConnectorOverlapAction' />"
400         "  </toolbar>"
402         "</ui>"
405 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
407 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
409 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
410 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
412 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
413 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
415 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
416 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
418 /* Global text entry widgets necessary for update */
419 /* GtkWidget *dropper_rgb_entry,
420           *dropper_opacity_entry ; */
421 // should be made a private member once this is converted to class
423 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
424     connection->disconnect();
425     delete connection;
428 static void purge_repr_listener( GObject* obj, GObject* tbl )
430     (void)obj;
431     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
432     if (oldrepr) { // remove old listener
433         sp_repr_remove_listener_by_data(oldrepr, tbl);
434         Inkscape::GC::release(oldrepr);
435         oldrepr = 0;
436         g_object_set_data( tbl, "repr", NULL );
437     }
440 GtkWidget *
441 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
442                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
443                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
445     SPAction *action = verb->get_action(view);
446     if (!action) return NULL;
448     SPAction *doubleclick_action;
449     if (doubleclick_verb)
450         doubleclick_action = doubleclick_verb->get_action(view);
451     else
452         doubleclick_action = NULL;
454     /* fixme: Handle sensitive/unsensitive */
455     /* fixme: Implement sp_button_new_from_action */
456     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
457     gtk_widget_show(b);
458     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
460     return b;
463 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
464                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
466     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
469 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
470                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
472     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
476 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
478     SPAction* targetAction = SP_ACTION(user_data);
479     if ( targetAction ) {
480         sp_action_perform( targetAction, NULL );
481     }
484 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
486     if ( data ) {
487         GtkAction* act = GTK_ACTION(data);
488         gtk_action_set_sensitive( act, sensitive );
489     }
492 static SPActionEventVector action_event_vector = {
493     {NULL},
494     NULL,
495     NULL,
496     sp_action_action_set_sensitive,
497     NULL,
498     NULL
499 };
501 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
503     GtkAction* act = 0;
505     SPAction* targetAction = verb->get_action(view);
506     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
507     act = GTK_ACTION(inky);
508     gtk_action_set_sensitive( act, targetAction->sensitive );
510     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
512     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
513     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
515     return act;
518 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
520     Inkscape::UI::View::View *view = desktop;
521     gint verbsToUse[] = {
522         // disabled until we have icons for them:
523         //find
524         //SP_VERB_EDIT_TILE,
525         //SP_VERB_EDIT_UNTILE,
526         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
527         SP_VERB_DIALOG_DISPLAY,
528         SP_VERB_DIALOG_FILL_STROKE,
529         SP_VERB_DIALOG_NAMEDVIEW,
530         SP_VERB_DIALOG_TEXT,
531         SP_VERB_DIALOG_XML_EDITOR,
532         SP_VERB_EDIT_CLONE,
533         SP_VERB_EDIT_COPY,
534         SP_VERB_EDIT_CUT,
535         SP_VERB_EDIT_DUPLICATE,
536         SP_VERB_EDIT_PASTE,
537         SP_VERB_EDIT_REDO,
538         SP_VERB_EDIT_UNDO,
539         SP_VERB_EDIT_UNLINK_CLONE,
540         SP_VERB_FILE_EXPORT,
541         SP_VERB_FILE_IMPORT,
542         SP_VERB_FILE_NEW,
543         SP_VERB_FILE_OPEN,
544         SP_VERB_FILE_PRINT,
545         SP_VERB_FILE_SAVE,
546         SP_VERB_OBJECT_TO_CURVE,
547         SP_VERB_SELECTION_GROUP,
548         SP_VERB_SELECTION_OUTLINE,
549         SP_VERB_SELECTION_UNGROUP,
550         SP_VERB_ZOOM_1_1,
551         SP_VERB_ZOOM_1_2,
552         SP_VERB_ZOOM_2_1,
553         SP_VERB_ZOOM_DRAWING,
554         SP_VERB_ZOOM_IN,
555         SP_VERB_ZOOM_NEXT,
556         SP_VERB_ZOOM_OUT,
557         SP_VERB_ZOOM_PAGE,
558         SP_VERB_ZOOM_PAGE_WIDTH,
559         SP_VERB_ZOOM_PREV,
560         SP_VERB_ZOOM_SELECTION,
561     };
563     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
564     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
566     static std::map<SPDesktop*, GtkActionGroup*> groups;
567     GtkActionGroup* mainActions = 0;
568     if ( groups.find(desktop) != groups.end() ) {
569         mainActions = groups[desktop];
570     }
572     if ( !mainActions ) {
573         mainActions = gtk_action_group_new("main");
574         groups[desktop] = mainActions;
575     }
577     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
578         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
579         if ( verb ) {
580             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
581                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
582                 gtk_action_group_add_action( mainActions, act );
583             }
584         }
585     }
587     return mainActions;
591 GtkWidget *
592 sp_tool_toolbox_new()
594     GtkTooltips *tt = gtk_tooltips_new();
595     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
597     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
598     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
600     gtk_widget_set_sensitive(tb, FALSE);
602     GtkWidget *hb = gtk_handle_box_new();
603     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
604     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
605     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
607     gtk_container_add(GTK_CONTAINER(hb), tb);
608     gtk_widget_show(GTK_WIDGET(tb));
610     sigc::connection* conn = new sigc::connection;
611     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
613     return hb;
616 static void
617 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
619     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
620     gtk_widget_queue_resize(child);
623 static void
624 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
626     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
627     gtk_widget_queue_resize(child);
630 GtkWidget *
631 sp_aux_toolbox_new()
633     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
635     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
637     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
639     gtk_widget_set_sensitive(tb, FALSE);
641     GtkWidget *hb = gtk_handle_box_new();
642     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
643     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
644     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
646     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
647     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
649     gtk_container_add(GTK_CONTAINER(hb), tb);
650     gtk_widget_show(GTK_WIDGET(tb));
652     sigc::connection* conn = new sigc::connection;
653     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
655     return hb;
658 //####################################
659 //# Commands Bar
660 //####################################
662 GtkWidget *
663 sp_commands_toolbox_new()
665     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
667     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
669     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
670     gtk_widget_set_sensitive(tb, FALSE);
672     GtkWidget *hb = gtk_handle_box_new();
673     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
674     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
675     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
677     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
678     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
680     gtk_container_add(GTK_CONTAINER(hb), tb);
681     gtk_widget_show(GTK_WIDGET(tb));
683     sigc::connection* conn = new sigc::connection;
684     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
686     return hb;
689 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
690                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
691                                                        gchar const *path, gchar const *data, gdouble def,
692                                                        GtkWidget *focusTarget,
693                                                        GtkWidget *us,
694                                                        GObject *dataKludge,
695                                                        gboolean altx, gchar const *altx_mark,
696                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
697                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
698                                                        void (*callback)(GtkAdjustment *, GObject *),
699                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
701     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
702                                                              lower, upper, step, page, page ) );
703     if (us) {
704         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
705     }
707     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
709     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
710     if ( shortLabel ) {
711         g_object_set( act, "short_label", shortLabel, NULL );
712     }
714     if ( (descrCount > 0) && descrLabels && descrValues ) {
715         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
716     }
718     if ( focusTarget ) {
719         ege_adjustment_action_set_focuswidget( act, focusTarget );
720     }
722     if ( altx && altx_mark ) {
723         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
724     }
726     if ( dataKludge ) {
727         g_object_set_data( dataKludge, data, adj );
728     }
730     // Using a cast just to make sure we pass in the right kind of function pointer
731     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
733     return act;
737 //####################################
738 //# node editing callbacks
739 //####################################
741 /**
742  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
743  */
744 static ShapeEditor *get_current_shape_editor()
746     if (!SP_ACTIVE_DESKTOP) {
747         return NULL;
748     }
750     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
752     if (!SP_IS_NODE_CONTEXT(event_context)) {
753         return NULL;
754     }
756     return SP_NODE_CONTEXT(event_context)->shape_editor;
760 void
761 sp_node_path_edit_add(void)
763     ShapeEditor *shape_editor = get_current_shape_editor();
764     if (shape_editor) shape_editor->add_node();
767 void
768 sp_node_path_edit_delete(void)
770     ShapeEditor *shape_editor = get_current_shape_editor();
771     if (shape_editor) shape_editor->delete_nodes();
774 void
775 sp_node_path_edit_delete_segment(void)
777     ShapeEditor *shape_editor = get_current_shape_editor();
778     if (shape_editor) shape_editor->delete_segment();
781 void
782 sp_node_path_edit_break(void)
784     ShapeEditor *shape_editor = get_current_shape_editor();
785     if (shape_editor) shape_editor->break_at_nodes();
788 void
789 sp_node_path_edit_join(void)
791     ShapeEditor *shape_editor = get_current_shape_editor();
792     if (shape_editor) shape_editor->join_nodes();
795 void
796 sp_node_path_edit_join_segment(void)
798     ShapeEditor *shape_editor = get_current_shape_editor();
799     if (shape_editor) shape_editor->join_segments();
802 void
803 sp_node_path_edit_toline(void)
805     ShapeEditor *shape_editor = get_current_shape_editor();
806     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
809 void
810 sp_node_path_edit_tocurve(void)
812     ShapeEditor *shape_editor = get_current_shape_editor();
813     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
816 void
817 sp_node_path_edit_cusp(void)
819     ShapeEditor *shape_editor = get_current_shape_editor();
820     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
823 void
824 sp_node_path_edit_smooth(void)
826     ShapeEditor *shape_editor = get_current_shape_editor();
827     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
830 void
831 sp_node_path_edit_symmetrical(void)
833     ShapeEditor *shape_editor = get_current_shape_editor();
834     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
837 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
838     bool show = gtk_toggle_action_get_active( act );
839     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
840     ShapeEditor *shape_editor = get_current_shape_editor();
841     if (shape_editor) shape_editor->show_handles(show);
844 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
845     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
848 /* is called when the node selection is modified */
849 static void
850 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
852     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
853     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
854     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
855     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
857     // quit if run by the attr_changed listener
858     if (g_object_get_data( tbl, "freeze" )) {
859         return;
860     }
862     // in turn, prevent listener from responding
863     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
865     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
866     SPUnit const *unit = tracker->getActiveUnit();
868     ShapeEditor *shape_editor = get_current_shape_editor();
869     if (shape_editor && shape_editor->has_nodepath()) {
870         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
871         int n_selected = 0;
872         if (nodepath) {
873             n_selected = nodepath->numSelected();
874         }
876         if (n_selected == 0) {
877             gtk_action_set_sensitive(xact, FALSE);
878             gtk_action_set_sensitive(yact, FALSE);
879         } else {
880             gtk_action_set_sensitive(xact, TRUE);
881             gtk_action_set_sensitive(yact, TRUE);
882             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
883             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
885             if (n_selected == 1) {
886                 NR::Point sel_node = nodepath->singleSelectedCoords();
887                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
888                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
889                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
890                 }
891             } else {
892                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
893                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
894                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
895                     /* Note: Currently x and y will always have a value, even if the coordinates of the
896                        selected nodes don't coincide (in this case we use the coordinates of the center
897                        of the bounding box). So the entries are never set to zero. */
898                     // FIXME: Maybe we should clear the entry if several nodes are selected
899                     //        instead of providing a kind of average value
900                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
901                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
902                 }
903             }
904         }
905     } else {
906         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
907         gtk_action_set_sensitive(xact, FALSE);
908         gtk_action_set_sensitive(yact, FALSE);
909     }
911     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
914 static void
915 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
917     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
919     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
920     SPUnit const *unit = tracker->getActiveUnit();
922     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
923         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
924     }
926     // quit if run by the attr_changed listener
927     if (g_object_get_data( tbl, "freeze" )) {
928         return;
929     }
931     // in turn, prevent listener from responding
932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
934     ShapeEditor *shape_editor = get_current_shape_editor();
935     if (shape_editor && shape_editor->has_nodepath()) {
936         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
937         if (!strcmp(value_name, "x")) {
938             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
939         }
940         if (!strcmp(value_name, "y")) {
941             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
942         }
943     }
945     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
948 static void
949 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
951     sp_node_path_value_changed(adj, tbl, "x");
954 static void
955 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
957     sp_node_path_value_changed(adj, tbl, "y");
960 //################################
961 //##    Node Editing Toolbox    ##
962 //################################
964 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
966     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
967     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
968     g_object_set_data( holder, "tracker", tracker );
970     {
971         InkAction* inky = ink_action_new( "NodeInsertAction",
972                                           _("Insert node"),
973                                           _("Insert new nodes into selected segments"),
974                                           "node_insert",
975                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
976         g_object_set( inky, "short_label", _("Insert"), NULL );
977         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
978         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
979     }
981     {
982         InkAction* inky = ink_action_new( "NodeDeleteAction",
983                                           _("Delete node"),
984                                           _("Delete selected nodes"),
985                                           "node_delete",
986                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
987         g_object_set( inky, "short_label", _("Delete"), NULL );
988         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
989         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
990     }
992     {
993         InkAction* inky = ink_action_new( "NodeJoinAction",
994                                           _("Join endnodes"),
995                                           _("Join selected endnodes"),
996                                           "node_join",
997                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
998         g_object_set( inky, "short_label", _("Join"), NULL );
999         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1000         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1001     }
1003     {
1004         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1005                                           _("Join Segment"),
1006                                           _("Join selected endnodes with a new segment"),
1007                                           "node_join_segment",
1008                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1009         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1010         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1011     }
1013     {
1014         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1015                                           _("Delete Segment"),
1016                                           _("Split path between two non-endpoint nodes"),
1017                                           "node_delete_segment",
1018                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1019         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1020         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1021     }
1023     {
1024         InkAction* inky = ink_action_new( "NodeBreakAction",
1025                                           _("Node Break"),
1026                                           _("Break path at selected nodes"),
1027                                           "node_break",
1028                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1029         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1030         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1031     }
1033     {
1034         InkAction* inky = ink_action_new( "NodeCuspAction",
1035                                           _("Node Cusp"),
1036                                           _("Make selected nodes corner"),
1037                                           "node_cusp",
1038                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1039         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1040         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1041     }
1043     {
1044         InkAction* inky = ink_action_new( "NodeSmoothAction",
1045                                           _("Node Smooth"),
1046                                           _("Make selected nodes smooth"),
1047                                           "node_smooth",
1048                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1049         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1050         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1051     }
1053     {
1054         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1055                                           _("Node Symmetric"),
1056                                           _("Make selected nodes symmetric"),
1057                                           "node_symmetric",
1058                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1059         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1060         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1061     }
1063     {
1064         InkAction* inky = ink_action_new( "NodeLineAction",
1065                                           _("Node Line"),
1066                                           _("Make selected segments lines"),
1067                                           "node_line",
1068                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1069         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1070         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1071     }
1073     {
1074         InkAction* inky = ink_action_new( "NodeCurveAction",
1075                                           _("Node Curve"),
1076                                           _("Make selected segments curves"),
1077                                           "node_curve",
1078                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1079         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1080         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1081     }
1083     {
1084         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1085                                                       _("Show Handles"),
1086                                                       _("Show the Bezier handles of selected nodes"),
1087                                                       "nodes_show_handles",
1088                                                       Inkscape::ICON_SIZE_DECORATION );
1089         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1090         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1091         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1092     }
1094     {
1095         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1096                                           _("Next Path Effect Parameter"),
1097                                           _("Show next Path Effect parameter for editing"),
1098                                           "edit_next_parameter",
1099                                           Inkscape::ICON_SIZE_DECORATION );
1100         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1101         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1102     }
1104     /* X coord of selected node(s) */
1105     {
1106         EgeAdjustmentAction* eact = 0;
1107         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1108         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1109         eact = create_adjustment_action( "NodeXAction",
1110                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1111                                          "tools.nodes", "Xcoord", 0,
1112                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1113                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1114                                          labels, values, G_N_ELEMENTS(labels),
1115                                          sp_node_path_x_value_changed );
1116         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1117         g_object_set_data( holder, "nodes_x_action", eact );
1118         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1119         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1120     }
1122     /* Y coord of selected node(s) */
1123     {
1124         EgeAdjustmentAction* eact = 0;
1125         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1126         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1127         eact = create_adjustment_action( "NodeYAction",
1128                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1129                                          "tools.nodes", "Ycoord", 0,
1130                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1131                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1132                                          labels, values, G_N_ELEMENTS(labels),
1133                                          sp_node_path_y_value_changed );
1134         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1135         g_object_set_data( holder, "nodes_y_action", eact );
1136         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1137         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1138     }
1140     // add the units menu
1141     {
1142         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1143         gtk_action_group_add_action( mainActions, act );
1144     }
1146     sigc::connection *connection = new sigc::connection (
1147         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1148         );
1150     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1151     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1152 } // end of sp_node_toolbox_prep()
1155 //########################
1156 //##    Zoom Toolbox    ##
1157 //########################
1159 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1161     // no custom GtkAction setup needed
1162 } // end of sp_zoom_toolbox_prep()
1164 void
1165 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1167     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")));
1171 void
1172 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1174     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")));
1177 void
1178 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1180     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")));
1183 static void
1184 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1186     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1187     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1189     if (old_desktop) {
1190         GList *children, *iter;
1192         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1193         for ( iter = children ; iter ; iter = iter->next ) {
1194             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1195         }
1196         g_list_free(children);
1197     }
1199     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1201     if (desktop) {
1202         gtk_widget_set_sensitive(toolbox, TRUE);
1203         setup_func(toolbox, desktop);
1204         update_func(desktop, desktop->event_context, toolbox);
1205         *conn = desktop->connectEventContextChanged
1206             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1207     } else {
1208         gtk_widget_set_sensitive(toolbox, FALSE);
1209     }
1211 } // end of toolbox_set_desktop()
1214 static void
1215 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1217     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1218     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1219     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1220         // "toolbox.tools" was not set. Fallback to older value
1221         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1223         // Copy the setting forwards
1224         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1225     }
1226     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1228     for (int i = 0 ; tools[i].type_name ; i++ ) {
1229         GtkWidget *button =
1230             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1231                                                               SP_BUTTON_TYPE_TOGGLE,
1232                                                               Inkscape::Verb::get(tools[i].verb),
1233                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1234                                                               desktop,
1235                                                               tooltips );
1237         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1238                            (gpointer)button );
1239     }
1243 static void
1244 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1246     gchar const *const tname = ( eventcontext
1247                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1248                                  : NULL );
1249     for (int i = 0 ; tools[i].type_name ; i++ ) {
1250         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1251         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1252     }
1255 static void
1256 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1258     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1259     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1260     GtkUIManager* mgr = gtk_ui_manager_new();
1261     GError* errVal = 0;
1262     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1263     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1265     std::map<std::string, GtkWidget*> dataHolders;
1267     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1268         if ( aux_toolboxes[i].prep_func ) {
1269             // converted to GtkActions and UIManager
1271             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1272             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1273             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1274             dataHolders[aux_toolboxes[i].type_name] = kludge;
1275             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1276         } else {
1278             GtkWidget *sub_toolbox = 0;
1279             if (aux_toolboxes[i].create_func == NULL)
1280                 sub_toolbox = sp_empty_toolbox_new(desktop);
1281             else {
1282                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1283             }
1285             gtk_size_group_add_widget( grouper, sub_toolbox );
1287             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1288             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1290         }
1291     }
1293     // Second pass to create toolbars *after* all GtkActions are created
1294     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1295         if ( aux_toolboxes[i].prep_func ) {
1296             // converted to GtkActions and UIManager
1298             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1300             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1301             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1303             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1304             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1305             g_free( tmp );
1306             tmp = 0;
1308             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1309             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1310             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1311                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1312             }
1313             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1316             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1318             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1319                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1320                 swatch->setDesktop( desktop );
1321                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1322                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1323                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1324                 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 );
1325             }
1327             gtk_widget_show_all( holder );
1328             sp_set_font_size_smaller( holder );
1330             gtk_size_group_add_widget( grouper, holder );
1332             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1333             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1334         }
1335     }
1337     g_object_unref( G_OBJECT(grouper) );
1340 static void
1341 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1343     gchar const *tname = ( eventcontext
1344                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1345                            : NULL );
1346     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1347         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1348         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1349             gtk_widget_show_all(sub_toolbox);
1350             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1351         } else {
1352             gtk_widget_hide(sub_toolbox);
1353         }
1354     }
1357 static void
1358 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1360     gchar const * descr =
1361         "<ui>"
1362         "  <toolbar name='CommandsToolbar'>"
1363         "    <toolitem action='FileNew' />"
1364         "    <toolitem action='FileOpen' />"
1365         "    <toolitem action='FileSave' />"
1366         "    <toolitem action='FilePrint' />"
1367         "    <separator />"
1368         "    <toolitem action='FileImport' />"
1369         "    <toolitem action='FileExport' />"
1370         "    <separator />"
1371         "    <toolitem action='EditUndo' />"
1372         "    <toolitem action='EditRedo' />"
1373         "    <separator />"
1374         "    <toolitem action='EditCopy' />"
1375         "    <toolitem action='EditCut' />"
1376         "    <toolitem action='EditPaste' />"
1377         "    <separator />"
1378         "    <toolitem action='ZoomSelection' />"
1379         "    <toolitem action='ZoomDrawing' />"
1380         "    <toolitem action='ZoomPage' />"
1381         "    <separator />"
1382         "    <toolitem action='EditDuplicate' />"
1383         "    <toolitem action='EditClone' />"
1384         "    <toolitem action='EditUnlinkClone' />"
1385         "    <separator />"
1386         "    <toolitem action='SelectionGroup' />"
1387         "    <toolitem action='SelectionUnGroup' />"
1388         "    <separator />"
1389         "    <toolitem action='DialogFillStroke' />"
1390         "    <toolitem action='DialogText' />"
1391         "    <toolitem action='DialogXMLEditor' />"
1392         "    <toolitem action='DialogAlignDistribute' />"
1393         "    <separator />"
1394         "    <toolitem action='DialogPreferences' />"
1395         "    <toolitem action='DialogDocumentProperties' />"
1396         "  </toolbar>"
1397         "</ui>";
1398     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1401     GtkUIManager* mgr = gtk_ui_manager_new();
1402     GError* errVal = 0;
1404     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1405     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1407     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1408     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1409         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1410     }
1411     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1412     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1413     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1416     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1419 static void
1420 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1424 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1426     gtk_widget_show(toolbox_toplevel);
1427     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1429     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1430     if (!shown_toolbox) {
1431         return;
1432     }
1433     gtk_widget_show(toolbox);
1435     gtk_widget_show_all(shown_toolbox);
1438 void
1439 aux_toolbox_space(GtkWidget *tb, gint space)
1441     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1444 static GtkWidget *
1445 sp_empty_toolbox_new(SPDesktop *desktop)
1447     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1448     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1449     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1451     gtk_widget_show_all(tbl);
1452     sp_set_font_size_smaller (tbl);
1454     return tbl;
1457 // helper UI functions
1459 GtkWidget *
1460 sp_tb_spinbutton(
1461     gchar *label, gchar const *tooltip,
1462     gchar const *path, gchar const *data, gdouble def,
1463     GtkWidget *us,
1464     GtkWidget *tbl,
1465     gboolean altx, gchar const *altx_mark,
1466     gdouble lower, gdouble upper, gdouble step, gdouble page,
1467     void (*callback)(GtkAdjustment *, GtkWidget *),
1468     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1470     GtkTooltips *tt = gtk_tooltips_new();
1472     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1474     GtkWidget *l = gtk_label_new(label);
1475     gtk_widget_show(l);
1476     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1477     gtk_container_add(GTK_CONTAINER(hb), l);
1479     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1480                                       lower, upper, step, page, page);
1481     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1482     if (us)
1483         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1485     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1486     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1487     if (altx)
1488         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1489     gtk_widget_set_size_request(sb,
1490                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1491                                 AUX_SPINBUTTON_HEIGHT);
1492     gtk_widget_show(sb);
1493     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1494     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1495     gtk_container_add(GTK_CONTAINER(hb), sb);
1496     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1498     return hb;
1501 #define MODE_LABEL_WIDTH 70
1503 //########################
1504 //##       Star         ##
1505 //########################
1507 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1509     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1511     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1512         // do not remember prefs if this call is initiated by an undo change, because undoing object
1513         // creation sets bogus values to its attributes before it is deleted
1514         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1515     }
1517     // quit if run by the attr_changed listener
1518     if (g_object_get_data( dataKludge, "freeze" )) {
1519         return;
1520     }
1522     // in turn, prevent listener from responding
1523     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1525     bool modmade = false;
1527     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1528     GSList const *items = selection->itemList();
1529     for (; items != NULL; items = items->next) {
1530         if (SP_IS_STAR((SPItem *) items->data)) {
1531             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1532             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1533             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1534                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1535                                     + M_PI / (gint)adj->value));
1536             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1537             modmade = true;
1538         }
1539     }
1540     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1541                                    _("Star: Change number of corners"));
1543     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1546 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1548     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1550     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1551         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1552     }
1554     // quit if run by the attr_changed listener
1555     if (g_object_get_data( dataKludge, "freeze" )) {
1556         return;
1557     }
1559     // in turn, prevent listener from responding
1560     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1562     bool modmade = false;
1563     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1564     GSList const *items = selection->itemList();
1565     for (; items != NULL; items = items->next) {
1566         if (SP_IS_STAR((SPItem *) items->data)) {
1567             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1569             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1570             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1571             if (r2 < r1) {
1572                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1573             } else {
1574                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1575             }
1577             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1578             modmade = true;
1579         }
1580     }
1582     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1583                                    _("Star: Change spoke ratio"));
1585     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1588 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1590     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1591     bool flat = ege_select_one_action_get_active( act ) == 0;
1593     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1594         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1595                                     flat ? "true" : "false" );
1596     }
1598     // quit if run by the attr_changed listener
1599     if (g_object_get_data( dataKludge, "freeze" )) {
1600         return;
1601     }
1603     // in turn, prevent listener from responding
1604     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1606     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1607     GSList const *items = selection->itemList();
1608     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1609     bool modmade = false;
1611     if ( prop_action ) {
1612         gtk_action_set_sensitive( prop_action, !flat );
1613     }
1615     for (; items != NULL; items = items->next) {
1616         if (SP_IS_STAR((SPItem *) items->data)) {
1617             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1618             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1619             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1620             modmade = true;
1621         }
1622     }
1624     if (modmade) {
1625         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1626                          flat ? _("Make polygon") : _("Make star"));
1627     }
1629     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1632 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1634     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1636     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1637         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1638     }
1640     // quit if run by the attr_changed listener
1641     if (g_object_get_data( dataKludge, "freeze" )) {
1642         return;
1643     }
1645     // in turn, prevent listener from responding
1646     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1648     bool modmade = false;
1650     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1651     GSList const *items = selection->itemList();
1652     for (; items != NULL; items = items->next) {
1653         if (SP_IS_STAR((SPItem *) items->data)) {
1654             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1655             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1656             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1657             modmade = true;
1658         }
1659     }
1660     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1661                                    _("Star: Change rounding"));
1663     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1666 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1668     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1670     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1671         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1672     }
1674     // quit if run by the attr_changed listener
1675     if (g_object_get_data( dataKludge, "freeze" )) {
1676         return;
1677     }
1679     // in turn, prevent listener from responding
1680     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1682     bool modmade = false;
1684     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1685     GSList const *items = selection->itemList();
1686     for (; items != NULL; items = items->next) {
1687         if (SP_IS_STAR((SPItem *) items->data)) {
1688             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1689             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1690             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1691             modmade = true;
1692         }
1693     }
1694     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1695                                    _("Star: Change randomization"));
1697     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1701 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1702                                        gchar const */*old_value*/, gchar const */*new_value*/,
1703                                        bool /*is_interactive*/, gpointer data)
1705     GtkWidget *tbl = GTK_WIDGET(data);
1707     // quit if run by the _changed callbacks
1708     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1709         return;
1710     }
1712     // in turn, prevent callbacks from responding
1713     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1715     GtkAdjustment *adj = 0;
1717     if (!strcmp(name, "inkscape:randomized")) {
1718         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1719         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1720     } else if (!strcmp(name, "inkscape:rounded")) {
1721         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1722         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1723     } else if (!strcmp(name, "inkscape:flatsided")) {
1724         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1725         char const *flatsides = repr->attribute("inkscape:flatsided");
1726         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1727         if ( flatsides && !strcmp(flatsides,"false") ) {
1728             ege_select_one_action_set_active( flat_action, 1 );
1729             gtk_action_set_sensitive( prop_action, TRUE );
1730         } else {
1731             ege_select_one_action_set_active( flat_action, 0 );
1732             gtk_action_set_sensitive( prop_action, FALSE );
1733         }
1734     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1735         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1736         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1737         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1738         if (r2 < r1) {
1739             gtk_adjustment_set_value(adj, r2/r1);
1740         } else {
1741             gtk_adjustment_set_value(adj, r1/r2);
1742         }
1743     } else if (!strcmp(name, "sodipodi:sides")) {
1744         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1745         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1746     }
1748     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1752 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1754     NULL, /* child_added */
1755     NULL, /* child_removed */
1756     star_tb_event_attr_changed,
1757     NULL, /* content_changed */
1758     NULL  /* order_changed */
1759 };
1762 /**
1763  *  \param selection Should not be NULL.
1764  */
1765 static void
1766 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1768     int n_selected = 0;
1769     Inkscape::XML::Node *repr = NULL;
1771     purge_repr_listener( tbl, tbl );
1773     for (GSList const *items = selection->itemList();
1774          items != NULL;
1775          items = items->next)
1776     {
1777         if (SP_IS_STAR((SPItem *) items->data)) {
1778             n_selected++;
1779             repr = SP_OBJECT_REPR((SPItem *) items->data);
1780         }
1781     }
1783     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1785     if (n_selected == 0) {
1786         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1787     } else if (n_selected == 1) {
1788         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1790         if (repr) {
1791             g_object_set_data( tbl, "repr", repr );
1792             Inkscape::GC::anchor(repr);
1793             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1794             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1795         }
1796     } else {
1797         // FIXME: implement averaging of all parameters for multiple selected stars
1798         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1799         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1800     }
1804 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1806     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1807     // callbacks to lump all the changes for all selected objects in one undo step
1809     GtkAdjustment *adj = 0;
1811     // fixme: make settable in prefs!
1812     gint mag = 5;
1813     gdouble prop = 0.5;
1814     gboolean flat = FALSE;
1815     gdouble randomized = 0;
1816     gdouble rounded = 0;
1818     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1819     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1821     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1822     gtk_action_set_sensitive( sb2, !flat );
1824     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1825     gtk_adjustment_set_value(adj, mag);
1826     gtk_adjustment_value_changed(adj);
1828     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1829     gtk_adjustment_set_value(adj, prop);
1830     gtk_adjustment_value_changed(adj);
1832     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1833     gtk_adjustment_set_value(adj, rounded);
1834     gtk_adjustment_value_changed(adj);
1836     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1837     gtk_adjustment_set_value(adj, randomized);
1838     gtk_adjustment_value_changed(adj);
1842 void
1843 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1845     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1846     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1847     GtkWidget *l = gtk_label_new(NULL);
1848     gtk_label_set_markup(GTK_LABEL(l), title);
1849     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1850     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1851     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1855 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1857     {
1858         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1859         ege_output_action_set_use_markup( act, TRUE );
1860         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1861         g_object_set_data( holder, "mode_action", act );
1862     }
1864     {
1865         EgeAdjustmentAction* eact = 0;
1866         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1867         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1869         /* Flatsided checkbox */
1870         {
1871             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1873             GtkTreeIter iter;
1874             gtk_list_store_append( model, &iter );
1875             gtk_list_store_set( model, &iter,
1876                                 0, _("Polygon"),
1877                                 1, _("Regular polygon (with one handle) instead of a star"),
1878                                 2, "star_flat",
1879                                 -1 );
1881             gtk_list_store_append( model, &iter );
1882             gtk_list_store_set( model, &iter,
1883                                 0, _("Star"),
1884                                 1, _("Star instead of a regular polygon (with one handle)"),
1885                                 2, "star_angled",
1886                                 -1 );
1888             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1889             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1890             g_object_set_data( holder, "flat_action", act );
1892             ege_select_one_action_set_appearance( act, "full" );
1893             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1894             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1895             ege_select_one_action_set_icon_column( act, 2 );
1896             ege_select_one_action_set_tooltip_column( act, 1  );
1898             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1899             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1900         }
1902         /* Magnitude */
1903         {
1904         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1905         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1906         eact = create_adjustment_action( "MagnitudeAction",
1907                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1908                                          "tools.shapes.star", "magnitude", 3,
1909                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1910                                          3, 1024, 1, 5,
1911                                          labels, values, G_N_ELEMENTS(labels),
1912                                          sp_stb_magnitude_value_changed,
1913                                          1.0, 0 );
1914         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1915         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1916         }
1918         /* Spoke ratio */
1919         {
1920         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1921         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1922         eact = create_adjustment_action( "SpokeAction",
1923                                          _("Spoke ratio"), _("Spoke ratio:"),
1924                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1925                                          // Base radius is the same for the closest handle.
1926                                          _("Base radius to tip radius ratio"),
1927                                          "tools.shapes.star", "proportion", 0.5,
1928                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1929                                          0.01, 1.0, 0.01, 0.1,
1930                                          labels, values, G_N_ELEMENTS(labels),
1931                                          sp_stb_proportion_value_changed );
1932         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1933         g_object_set_data( holder, "prop_action", eact );
1934         }
1936         if ( !isFlatSided ) {
1937             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1938         } else {
1939             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1940         }
1942         /* Roundedness */
1943         {
1944         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1945         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1946         eact = create_adjustment_action( "RoundednessAction",
1947                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1948                                          "tools.shapes.star", "rounded", 0.0,
1949                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1950                                          -10.0, 10.0, 0.01, 0.1,
1951                                          labels, values, G_N_ELEMENTS(labels),
1952                                          sp_stb_rounded_value_changed );
1953         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1954         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1955         }
1957         /* Randomization */
1958         {
1959         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1960         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1961         eact = create_adjustment_action( "RandomizationAction",
1962                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1963                                          "tools.shapes.star", "randomized", 0.0,
1964                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1965                                          -10.0, 10.0, 0.001, 0.01,
1966                                          labels, values, G_N_ELEMENTS(labels),
1967                                          sp_stb_randomized_value_changed, 0.1, 3 );
1968         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1969         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1970         }
1971     }
1973     {
1974         /* Reset */
1975         {
1976             GtkAction* act = gtk_action_new( "StarResetAction",
1977                                              _("Defaults"),
1978                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1979                                              GTK_STOCK_CLEAR );
1980             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1981             gtk_action_group_add_action( mainActions, act );
1982             gtk_action_set_sensitive( act, TRUE );
1983         }
1984     }
1986     sigc::connection *connection = new sigc::connection(
1987         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1988         );
1989     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1990     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1994 //########################
1995 //##       Rect         ##
1996 //########################
1998 static void sp_rtb_sensitivize( GObject *tbl )
2000     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2001     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2002     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2004     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2005         gtk_action_set_sensitive( not_rounded, FALSE );
2006     } else {
2007         gtk_action_set_sensitive( not_rounded, TRUE );
2008     }
2012 static void
2013 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2014                           void (*setter)(SPRect *, gdouble))
2016     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2018     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2019     SPUnit const *unit = tracker->getActiveUnit();
2021     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2022         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2023     }
2025     // quit if run by the attr_changed listener
2026     if (g_object_get_data( tbl, "freeze" )) {
2027         return;
2028     }
2030     // in turn, prevent listener from responding
2031     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2033     bool modmade = false;
2034     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2035     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2036         if (SP_IS_RECT(items->data)) {
2037             if (adj->value != 0) {
2038                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2039             } else {
2040                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2041             }
2042             modmade = true;
2043         }
2044     }
2046     sp_rtb_sensitivize( tbl );
2048     if (modmade) {
2049         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2050                                    _("Change rectangle"));
2051     }
2053     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2056 static void
2057 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2059     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2062 static void
2063 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2065     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2068 static void
2069 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2071     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2074 static void
2075 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2077     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2082 static void
2083 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2085     GtkAdjustment *adj = 0;
2087     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2088     gtk_adjustment_set_value(adj, 0.0);
2089     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2090     gtk_adjustment_value_changed(adj);
2092     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2093     gtk_adjustment_set_value(adj, 0.0);
2094     gtk_adjustment_value_changed(adj);
2096     sp_rtb_sensitivize( obj );
2099 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2100                                        gchar const */*old_value*/, gchar const */*new_value*/,
2101                                        bool /*is_interactive*/, gpointer data)
2103     GObject *tbl = G_OBJECT(data);
2105     // quit if run by the _changed callbacks
2106     if (g_object_get_data( tbl, "freeze" )) {
2107         return;
2108     }
2110     // in turn, prevent callbacks from responding
2111     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2113     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2114     SPUnit const *unit = tracker->getActiveUnit();
2116     gpointer item = g_object_get_data( tbl, "item" );
2117     if (item && SP_IS_RECT(item)) {
2118         {
2119             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2120             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2121             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2122         }
2124         {
2125             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2126             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2127             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2128         }
2130         {
2131             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2132             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2133             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2134         }
2136         {
2137             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2138             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2139             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2140         }
2141     }
2143     sp_rtb_sensitivize( tbl );
2145     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2149 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2150     NULL, /* child_added */
2151     NULL, /* child_removed */
2152     rect_tb_event_attr_changed,
2153     NULL, /* content_changed */
2154     NULL  /* order_changed */
2155 };
2157 /**
2158  *  \param selection should not be NULL.
2159  */
2160 static void
2161 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2163     int n_selected = 0;
2164     Inkscape::XML::Node *repr = NULL;
2165     SPItem *item = NULL;
2167     if ( g_object_get_data( tbl, "repr" ) ) {
2168         g_object_set_data( tbl, "item", NULL );
2169     }
2170     purge_repr_listener( tbl, tbl );
2172     for (GSList const *items = selection->itemList();
2173          items != NULL;
2174          items = items->next) {
2175         if (SP_IS_RECT((SPItem *) items->data)) {
2176             n_selected++;
2177             item = (SPItem *) items->data;
2178             repr = SP_OBJECT_REPR(item);
2179         }
2180     }
2182     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2184     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2186     if (n_selected == 0) {
2187         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2189         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2190         gtk_action_set_sensitive(w, FALSE);
2191         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2192         gtk_action_set_sensitive(h, FALSE);
2194     } else if (n_selected == 1) {
2195         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2196         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2198         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2199         gtk_action_set_sensitive(w, TRUE);
2200         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2201         gtk_action_set_sensitive(h, TRUE);
2203         if (repr) {
2204             g_object_set_data( tbl, "repr", repr );
2205             g_object_set_data( tbl, "item", item );
2206             Inkscape::GC::anchor(repr);
2207             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2208             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2209         }
2210     } else {
2211         // FIXME: implement averaging of all parameters for multiple selected
2212         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2213         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2214         sp_rtb_sensitivize( tbl );
2215     }
2219 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2221     EgeAdjustmentAction* eact = 0;
2223     {
2224         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2225         ege_output_action_set_use_markup( act, TRUE );
2226         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2227         g_object_set_data( holder, "mode_action", act );
2228     }
2230     // rx/ry units menu: create
2231     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2232     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2233     // fixme: add % meaning per cent of the width/height
2234     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2235     g_object_set_data( holder, "tracker", tracker );
2237     /* W */
2238     {
2239         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2240         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2241         eact = create_adjustment_action( "RectWidthAction",
2242                                          _("Width"), _("W:"), _("Width of rectangle"),
2243                                          "tools.shapes.rect", "width", 0,
2244                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2245                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2246                                          labels, values, G_N_ELEMENTS(labels),
2247                                          sp_rtb_width_value_changed );
2248         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2249         g_object_set_data( holder, "width_action", eact );
2250         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2251         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2252     }
2254     /* H */
2255     {
2256         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2257         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2258         eact = create_adjustment_action( "RectHeightAction",
2259                                          _("Height"), _("H:"), _("Height of rectangle"),
2260                                          "tools.shapes.rect", "height", 0,
2261                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2262                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2263                                          labels, values, G_N_ELEMENTS(labels),
2264                                          sp_rtb_height_value_changed );
2265         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2266         g_object_set_data( holder, "height_action", eact );
2267         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2268         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2269     }
2271     /* rx */
2272     {
2273         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2274         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2275         eact = create_adjustment_action( "RadiusXAction",
2276                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2277                                          "tools.shapes.rect", "rx", 0,
2278                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2279                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2280                                          labels, values, G_N_ELEMENTS(labels),
2281                                          sp_rtb_rx_value_changed);
2282         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2283         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2284     }
2286     /* ry */
2287     {
2288         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2289         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2290         eact = create_adjustment_action( "RadiusYAction",
2291                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2292                                          "tools.shapes.rect", "ry", 0,
2293                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2294                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2295                                          labels, values, G_N_ELEMENTS(labels),
2296                                          sp_rtb_ry_value_changed);
2297         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2298         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2299     }
2301     // add the units menu
2302     {
2303         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2304         gtk_action_group_add_action( mainActions, act );
2305     }
2307     /* Reset */
2308     {
2309         InkAction* inky = ink_action_new( "RectResetAction",
2310                                           _("Not rounded"),
2311                                           _("Make corners sharp"),
2312                                           "squared_corner",
2313                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2314         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2315         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2316         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2317         g_object_set_data( holder, "not_rounded", inky );
2318     }
2320     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2321     sp_rtb_sensitivize( holder );
2323     sigc::connection *connection = new sigc::connection(
2324         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2325         );
2326     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2327     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2330 //########################
2331 //##       3D Box       ##
2332 //########################
2334 // normalize angle so that it lies in the interval [0,360]
2335 static double box3d_normalize_angle (double a) {
2336     double angle = a + ((int) (a/360.0))*360;
2337     if (angle < 0) {
2338         angle += 360.0;
2339     }
2340     return angle;
2343 static void
2344 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2345                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2346     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2347     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2348     //       are reset).
2349     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2351     if (is_infinite) {
2352         gtk_toggle_action_set_active(tact, TRUE);
2353         gtk_action_set_sensitive(act, TRUE);
2355         double angle = persp3d_get_infinite_angle(persp, axis);
2356         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2357             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2358         }
2359     } else {
2360         gtk_toggle_action_set_active(tact, FALSE);
2361         gtk_action_set_sensitive(act, FALSE);
2362     }
2365 static void
2366 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2367     if (!persp_repr) {
2368         g_print ("No perspective given to box3d_resync_toolbar().\n");
2369         return;
2370     }
2372     GtkWidget *tbl = GTK_WIDGET(data);
2373     GtkAdjustment *adj = 0;
2374     GtkAction *act = 0;
2375     GtkToggleAction *tact = 0;
2376     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2377     {
2378         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2379         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2380         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2382         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2383     }
2384     {
2385         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2386         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2387         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2389         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2390     }
2391     {
2392         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2393         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2394         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2396         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2397     }
2400 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2401                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2402                                                   bool /*is_interactive*/, gpointer data)
2404     GtkWidget *tbl = GTK_WIDGET(data);
2406     // quit if run by the attr_changed listener
2407     // note: it used to work without the differently called freeze_ attributes (here and in
2408     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2409     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2410         return;
2411     }
2413     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2414     // sp_document_maybe_done() when the document is undo insensitive)
2415     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2417     // TODO: Only update the appropriate part of the toolbar
2418 //    if (!strcmp(name, "inkscape:vp_z")) {
2419         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2420 //    }
2422     Persp3D *persp = persp3d_get_from_repr(repr);
2423     persp3d_update_box_reprs(persp);
2425     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2428 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2430     NULL, /* child_added */
2431     NULL, /* child_removed */
2432     box3d_persp_tb_event_attr_changed,
2433     NULL, /* content_changed */
2434     NULL  /* order_changed */
2435 };
2437 /**
2438  *  \param selection Should not be NULL.
2439  */
2440 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2441 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2442 static void
2443 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2445     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2446     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2447     // update the perspectives with infinite VPs and leave the other ones untouched).
2449     Inkscape::XML::Node *persp_repr = NULL;
2450     purge_repr_listener(tbl, tbl);
2452     SPItem *item = selection->singleItem();
2453     if (item && SP_IS_BOX3D(item)) {
2454         // FIXME: Also deal with multiple selected boxes
2455         SPBox3D *box = SP_BOX3D(item);
2456         Persp3D *persp = box3d_get_perspective(box);
2457         persp_repr = SP_OBJECT_REPR(persp);
2458         if (persp_repr) {
2459             g_object_set_data(tbl, "repr", persp_repr);
2460             Inkscape::GC::anchor(persp_repr);
2461             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2462             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2463         }
2465         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2466         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2467     
2468         box3d_resync_toolbar(persp_repr, tbl);
2469     }
2472 static void
2473 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2475     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2476     SPDocument *document = sp_desktop_document(desktop);
2478     // quit if run by the attr_changed listener
2479     // note: it used to work without the differently called freeze_ attributes (here and in
2480     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2481     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2482         return;
2483     }
2485     // in turn, prevent listener from responding
2486     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2488     //Persp3D *persp = document->current_persp3d;
2489     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2490     if (sel_persps.empty()) {
2491         // this can happen when the document is created; we silently ignore it
2492         return;
2493     }
2494     Persp3D *persp = *(sel_persps.begin());
2496     persp->tmat.set_infinite_direction (axis, adj->value);
2497     SP_OBJECT(persp)->updateRepr();
2499     // TODO: use the correct axis here, too
2500     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2502     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2506 static void
2507 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2509     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2512 static void
2513 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2515     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2518 static void
2519 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2521     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2525 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2527     // TODO: Take all selected perspectives into account
2528     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2529     if (sel_persps.empty()) {
2530         // this can happen when the document is created; we silently ignore it
2531         return;
2532     }
2533     Persp3D *persp = *(sel_persps.begin());
2535     bool set_infinite = gtk_toggle_action_get_active(act);
2536     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2539 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2541     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2544 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2546     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2549 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2551     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2554 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2556     EgeAdjustmentAction* eact = 0;
2557     SPDocument *document = sp_desktop_document (desktop);
2558     Persp3D *persp = document->current_persp3d;
2560     EgeAdjustmentAction* box3d_angle_x = 0;
2561     EgeAdjustmentAction* box3d_angle_y = 0;
2562     EgeAdjustmentAction* box3d_angle_z = 0;
2564     /* Angle X */
2565     {
2566         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2567         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2568         eact = create_adjustment_action( "3DBoxAngleXAction",
2569                                          _("Angle in X direction"), _("Angle X:"),
2570                                          // Translators: PL is short for 'perspective line'
2571                                          _("Angle of PLs in X direction"),
2572                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2573                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2574                                          -360.0, 360.0, 1.0, 10.0,
2575                                          labels, values, G_N_ELEMENTS(labels),
2576                                          box3d_angle_x_value_changed );
2577         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2578         g_object_set_data( holder, "box3d_angle_x_action", eact );
2579         box3d_angle_x = eact;
2580     }
2582     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2583         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2584     } else {
2585         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2586     }
2589     /* VP X state */
2590     {
2591         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2592                                                       // Translators: VP is short for 'vanishing point'
2593                                                       _("State of VP in X direction"),
2594                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2595                                                       "toggle_vp_x",
2596                                                       Inkscape::ICON_SIZE_DECORATION );
2597         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2598         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2599         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2600         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2601         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2602     }
2604     /* Angle Y */
2605     {
2606         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2607         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2608         eact = create_adjustment_action( "3DBoxAngleYAction",
2609                                          _("Angle in Y direction"), _("Angle Y:"),
2610                                          // Translators: PL is short for 'perspective line'
2611                                          _("Angle of PLs in Y direction"),
2612                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2613                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2614                                          -360.0, 360.0, 1.0, 10.0,
2615                                          labels, values, G_N_ELEMENTS(labels),
2616                                          box3d_angle_y_value_changed );
2617         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2618         g_object_set_data( holder, "box3d_angle_y_action", eact );
2619         box3d_angle_y = eact;
2620     }
2622     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2623         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2624     } else {
2625         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2626     }
2628     /* VP Y state */
2629     {
2630         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2631                                                       // Translators: VP is short for 'vanishing point'
2632                                                       _("State of VP in Y direction"),
2633                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2634                                                       "toggle_vp_y",
2635                                                       Inkscape::ICON_SIZE_DECORATION );
2636         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2637         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2638         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2639         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2640         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2641     }
2643     /* Angle Z */
2644     {
2645         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2646         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2647         eact = create_adjustment_action( "3DBoxAngleZAction",
2648                                          _("Angle in Z direction"), _("Angle Z:"),
2649                                          // Translators: PL is short for 'perspective line'
2650                                          _("Angle of PLs in Z direction"),
2651                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2652                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2653                                          -360.0, 360.0, 1.0, 10.0,
2654                                          labels, values, G_N_ELEMENTS(labels),
2655                                          box3d_angle_z_value_changed );
2656         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2657         g_object_set_data( holder, "box3d_angle_z_action", eact );
2658         box3d_angle_z = eact;
2659     }
2661     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2662         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2663     } else {
2664         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2665     }
2667     /* VP Z state */
2668     {
2669         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2670                                                       // Translators: VP is short for 'vanishing point'
2671                                                       _("State of VP in Z direction"),
2672                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2673                                                       "toggle_vp_z",
2674                                                       Inkscape::ICON_SIZE_DECORATION );
2675         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2676         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2677         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2678         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2679         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2680     }
2682     sigc::connection *connection = new sigc::connection(
2683         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2684        );
2685     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2686     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2689 //########################
2690 //##       Spiral       ##
2691 //########################
2693 static void
2694 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2696     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2698     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2699         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2700     }
2702     // quit if run by the attr_changed listener
2703     if (g_object_get_data( tbl, "freeze" )) {
2704         return;
2705     }
2707     // in turn, prevent listener from responding
2708     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2710     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2712     bool modmade = false;
2713     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2714          items != NULL;
2715          items = items->next)
2716     {
2717         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2718             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2719             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2720             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2721             modmade = true;
2722         }
2723     }
2725     g_free(namespaced_name);
2727     if (modmade) {
2728         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2729                                    _("Change spiral"));
2730     }
2732     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2735 static void
2736 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2738     sp_spl_tb_value_changed(adj, tbl, "revolution");
2741 static void
2742 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2744     sp_spl_tb_value_changed(adj, tbl, "expansion");
2747 static void
2748 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2750     sp_spl_tb_value_changed(adj, tbl, "t0");
2753 static void
2754 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2756     GtkWidget *tbl = GTK_WIDGET(obj);
2758     GtkAdjustment *adj;
2760     // fixme: make settable
2761     gdouble rev = 5;
2762     gdouble exp = 1.0;
2763     gdouble t0 = 0.0;
2765     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2766     gtk_adjustment_set_value(adj, rev);
2767     gtk_adjustment_value_changed(adj);
2769     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2770     gtk_adjustment_set_value(adj, exp);
2771     gtk_adjustment_value_changed(adj);
2773     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2774     gtk_adjustment_set_value(adj, t0);
2775     gtk_adjustment_value_changed(adj);
2777     spinbutton_defocus(GTK_OBJECT(tbl));
2781 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2782                                          gchar const */*old_value*/, gchar const */*new_value*/,
2783                                          bool /*is_interactive*/, gpointer data)
2785     GtkWidget *tbl = GTK_WIDGET(data);
2787     // quit if run by the _changed callbacks
2788     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2789         return;
2790     }
2792     // in turn, prevent callbacks from responding
2793     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2795     GtkAdjustment *adj;
2796     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2797     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2799     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2800     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2802     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2803     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2805     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2809 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2810     NULL, /* child_added */
2811     NULL, /* child_removed */
2812     spiral_tb_event_attr_changed,
2813     NULL, /* content_changed */
2814     NULL  /* order_changed */
2815 };
2817 static void
2818 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2820     int n_selected = 0;
2821     Inkscape::XML::Node *repr = NULL;
2823     purge_repr_listener( tbl, tbl );
2825     for (GSList const *items = selection->itemList();
2826          items != NULL;
2827          items = items->next)
2828     {
2829         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2830             n_selected++;
2831             repr = SP_OBJECT_REPR((SPItem *) items->data);
2832         }
2833     }
2835     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2837     if (n_selected == 0) {
2838         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2839     } else if (n_selected == 1) {
2840         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2842         if (repr) {
2843             g_object_set_data( tbl, "repr", repr );
2844             Inkscape::GC::anchor(repr);
2845             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2846             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2847         }
2848     } else {
2849         // FIXME: implement averaging of all parameters for multiple selected
2850         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2851         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2852     }
2856 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2858     EgeAdjustmentAction* eact = 0;
2860     {
2861         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2862         ege_output_action_set_use_markup( act, TRUE );
2863         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2864         g_object_set_data( holder, "mode_action", act );
2865     }
2867     /* Revolution */
2868     {
2869         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2870         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2871         eact = create_adjustment_action( "SpiralRevolutionAction",
2872                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2873                                          "tools.shapes.spiral", "revolution", 3.0,
2874                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2875                                          0.01, 1024.0, 0.1, 1.0,
2876                                          labels, values, G_N_ELEMENTS(labels),
2877                                          sp_spl_tb_revolution_value_changed, 1, 2);
2878         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2879     }
2881     /* Expansion */
2882     {
2883         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2884         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2885         eact = create_adjustment_action( "SpiralExpansionAction",
2886                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2887                                          "tools.shapes.spiral", "expansion", 1.0,
2888                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2889                                          0.0, 1000.0, 0.01, 1.0,
2890                                          labels, values, G_N_ELEMENTS(labels),
2891                                          sp_spl_tb_expansion_value_changed);
2892         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2893     }
2895     /* T0 */
2896     {
2897         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2898         gdouble values[] = {0, 0.5, 0.9};
2899         eact = create_adjustment_action( "SpiralT0Action",
2900                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2901                                          "tools.shapes.spiral", "t0", 0.0,
2902                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2903                                          0.0, 0.999, 0.01, 1.0,
2904                                          labels, values, G_N_ELEMENTS(labels),
2905                                          sp_spl_tb_t0_value_changed);
2906         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2907     }
2909     /* Reset */
2910     {
2911         InkAction* inky = ink_action_new( "SpiralResetAction",
2912                                           _("Defaults"),
2913                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2914                                           GTK_STOCK_CLEAR,
2915                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2916         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2917         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2918     }
2921     sigc::connection *connection = new sigc::connection(
2922         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2923         );
2924     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2925     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2928 //########################
2929 //##     Pen/Pencil    ##
2930 //########################
2933 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2935     // Put stuff here
2938 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2940     // Put stuff here
2943 //########################
2944 //##       Tweak        ##
2945 //########################
2947 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2949     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2952 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2954     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2957 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2959     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2962 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2964     int mode = ege_select_one_action_get_active( act );
2965     prefs_set_int_attribute("tools.tweak", "mode", mode);
2967     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2968     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2969     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2970     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2971     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2972     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2973     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2974         if (doh) gtk_action_set_sensitive (doh, TRUE);
2975         if (dos) gtk_action_set_sensitive (dos, TRUE);
2976         if (dol) gtk_action_set_sensitive (dol, TRUE);
2977         if (doo) gtk_action_set_sensitive (doo, TRUE);
2978         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2979         if (fid) gtk_action_set_sensitive (fid, FALSE);
2980     } else {
2981         if (doh) gtk_action_set_sensitive (doh, FALSE);
2982         if (dos) gtk_action_set_sensitive (dos, FALSE);
2983         if (dol) gtk_action_set_sensitive (dol, FALSE);
2984         if (doo) gtk_action_set_sensitive (doo, FALSE);
2985         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2986         if (fid) gtk_action_set_sensitive (fid, TRUE);
2987     }
2990 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2992     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2995 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2996     bool show = gtk_toggle_action_get_active( act );
2997     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2999 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3000     bool show = gtk_toggle_action_get_active( act );
3001     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3003 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3004     bool show = gtk_toggle_action_get_active( act );
3005     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3007 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3008     bool show = gtk_toggle_action_get_active( act );
3009     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3012 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3014     {
3015         /* Width */
3016         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3017         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3018         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3019                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3020                                                               "tools.tweak", "width", 15,
3021                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3022                                                               1, 100, 1.0, 10.0,
3023                                                               labels, values, G_N_ELEMENTS(labels),
3024                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3025         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3026         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3027     }
3030     {
3031         /* Force */
3032         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3033         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3034         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3035                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3036                                                               "tools.tweak", "force", 20,
3037                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3038                                                               1, 100, 1.0, 10.0,
3039                                                               labels, values, G_N_ELEMENTS(labels),
3040                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3041         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3042         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3043     }
3045     /* Mode */
3046     {
3047         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3049         GtkTreeIter iter;
3050         gtk_list_store_append( model, &iter );
3051         gtk_list_store_set( model, &iter,
3052                             0, _("Push mode"),
3053                             1, _("Push parts of paths in any direction"),
3054                             2, "tweak_push_mode",
3055                             -1 );
3057         gtk_list_store_append( model, &iter );
3058         gtk_list_store_set( model, &iter,
3059                             0, _("Shrink mode"),
3060                             1, _("Shrink (inset) parts of paths"),
3061                             2, "tweak_shrink_mode",
3062                             -1 );
3064         gtk_list_store_append( model, &iter );
3065         gtk_list_store_set( model, &iter,
3066                             0, _("Grow mode"),
3067                             1, _("Grow (outset) parts of paths"),
3068                             2, "tweak_grow_mode",
3069                             -1 );
3071         gtk_list_store_append( model, &iter );
3072         gtk_list_store_set( model, &iter,
3073                             0, _("Attract mode"),
3074                             1, _("Attract parts of paths towards cursor"),
3075                             2, "tweak_attract_mode",
3076                             -1 );
3078         gtk_list_store_append( model, &iter );
3079         gtk_list_store_set( model, &iter,
3080                             0, _("Repel mode"),
3081                             1, _("Repel parts of paths from cursor"),
3082                             2, "tweak_repel_mode",
3083                             -1 );
3085         gtk_list_store_append( model, &iter );
3086         gtk_list_store_set( model, &iter,
3087                             0, _("Roughen mode"),
3088                             1, _("Roughen parts of paths"),
3089                             2, "tweak_roughen_mode",
3090                             -1 );
3092         gtk_list_store_append( model, &iter );
3093         gtk_list_store_set( model, &iter,
3094                             0, _("Color paint mode"),
3095                             1, _("Paint the tool's color upon selected objects"),
3096                             2, "tweak_colorpaint_mode",
3097                             -1 );
3099         gtk_list_store_append( model, &iter );
3100         gtk_list_store_set( model, &iter,
3101                             0, _("Color jitter mode"),
3102                             1, _("Jitter the colors of selected objects"),
3103                             2, "tweak_colorjitter_mode",
3104                             -1 );
3106         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3107         g_object_set( act, "short_label", _("Mode:"), NULL );
3108         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3109         g_object_set_data( holder, "mode_action", act );
3111         ege_select_one_action_set_appearance( act, "full" );
3112         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3113         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3114         ege_select_one_action_set_icon_column( act, 2 );
3115         ege_select_one_action_set_tooltip_column( act, 1  );
3117         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3118         ege_select_one_action_set_active( act, mode );
3119         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3121         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3122     }
3124     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3126     {
3127         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3128         ege_output_action_set_use_markup( act, TRUE );
3129         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3130         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3131             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3132         g_object_set_data( holder, "tweak_channels_label", act);
3133     }
3135     {
3136         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3137                                                       _("Hue"),
3138                                                       _("In color mode, act on objects' hue"),
3139                                                       NULL,
3140                                                       Inkscape::ICON_SIZE_DECORATION );
3141         g_object_set( act, "short_label", _("H"), NULL );
3142         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3143         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3144         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3145         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3146             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3147         g_object_set_data( holder, "tweak_doh", act);
3148     }
3149     {
3150         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3151                                                       _("Saturation"),
3152                                                       _("In color mode, act on objects' saturation"),
3153                                                       NULL,
3154                                                       Inkscape::ICON_SIZE_DECORATION );
3155         g_object_set( act, "short_label", _("S"), NULL );
3156         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3157         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3158         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3159         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3160             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3161         g_object_set_data( holder, "tweak_dos", act );
3162     }
3163     {
3164         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3165                                                       _("Lightness"),
3166                                                       _("In color mode, act on objects' lightness"),
3167                                                       NULL,
3168                                                       Inkscape::ICON_SIZE_DECORATION );
3169         g_object_set( act, "short_label", _("L"), NULL );
3170         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3171         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3172         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3173         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3174             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3175         g_object_set_data( holder, "tweak_dol", act );
3176     }
3177     {
3178         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3179                                                       _("Opacity"),
3180                                                       _("In color mode, act on objects' opacity"),
3181                                                       NULL,
3182                                                       Inkscape::ICON_SIZE_DECORATION );
3183         g_object_set( act, "short_label", _("O"), NULL );
3184         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3185         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3186         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3187         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3188             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3189         g_object_set_data( holder, "tweak_doo", act );
3190     }
3192     {   /* Fidelity */
3193         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3194         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3195         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3196                                                               _("Fidelity"), _("Fidelity:"),
3197                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3198                                                               "tools.tweak", "fidelity", 50,
3199                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3200                                                               1, 100, 1.0, 10.0,
3201                                                               labels, values, G_N_ELEMENTS(labels),
3202                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3203         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3204         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3205         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3206             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3207         g_object_set_data( holder, "tweak_fidelity", eact );
3208     }
3211     /* Use Pressure button */
3212     {
3213         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3214                                                       _("Pressure"),
3215                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3216                                                       "use_pressure",
3217                                                       Inkscape::ICON_SIZE_DECORATION );
3218         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3219         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3220         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3221     }
3226 //########################
3227 //##     Calligraphy    ##
3228 //########################
3230 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3232     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3235 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3237     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3240 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3242     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3245 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3247     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3250 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3252     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3255 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3257     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3260 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3262     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3265 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3267     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3270 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3272     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3275 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3277     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3280 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3282     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3284     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3287 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3289     // FIXME: make defaults settable via Inkscape Options
3290     struct KeyValue {
3291         char const *key;
3292         double value;
3293     } const key_values[] = {
3294         {"mass", 0.02},
3295         {"wiggle", 0.0},
3296         {"angle", 30.0},
3297         {"width", 15},
3298         {"thinning", 0.1},
3299         {"tremor", 0.0},
3300         {"flatness", 0.9},
3301         {"cap_rounding", 0.0}
3302     };
3304     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3305         KeyValue const &kv = key_values[i];
3306         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3307         if ( adj ) {
3308             gtk_adjustment_set_value(adj, kv.value);
3309         }
3310     }
3314 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3316     {
3317         EgeAdjustmentAction* calligraphy_angle = 0;
3319         {
3320         /* Width */
3321         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3322         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3323         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3324                                                               _("Pen Width"), _("Width:"),
3325                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3326                                                               "tools.calligraphic", "width", 15,
3327                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3328                                                               1, 100, 1.0, 10.0,
3329                                                               labels, values, G_N_ELEMENTS(labels),
3330                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3331         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3332         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3333         }
3335         {
3336         /* Thinning */
3337             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3338             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3339         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3340                                                               _("Stroke Thinning"), _("Thinning:"),
3341                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3342                                                               "tools.calligraphic", "thinning", 0.1,
3343                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3344                                                               -1.0, 1.0, 0.01, 0.1,
3345                                                               labels, values, G_N_ELEMENTS(labels),
3346                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3347         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3348         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3349         }
3351         {
3352         /* Angle */
3353         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3354         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3355         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3356                                                               _("Pen Angle"), _("Angle:"),
3357                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3358                                                               "tools.calligraphic", "angle", 30,
3359                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3360                                                               -90.0, 90.0, 1.0, 10.0,
3361                                                               labels, values, G_N_ELEMENTS(labels),
3362                                                               sp_ddc_angle_value_changed, 1, 0 );
3363         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3364         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3365         calligraphy_angle = eact;
3366         }
3368         {
3369         /* Fixation */
3370             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3371         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3372         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3373                                                               _("Fixation"), _("Fixation:"),
3374                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3375                                                               "tools.calligraphic", "flatness", 0.9,
3376                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3377                                                               0.0, 1.0, 0.01, 0.1,
3378                                                               labels, values, G_N_ELEMENTS(labels),
3379                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3380         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3381         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3382         }
3384         {
3385         /* Cap Rounding */
3386             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3387         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3388         // TRANSLATORS: "cap" means "end" (both start and finish) here
3389         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3390                                                               _("Cap rounding"), _("Caps:"),
3391                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3392                                                               "tools.calligraphic", "cap_rounding", 0.0,
3393                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3394                                                               0.0, 5.0, 0.01, 0.1,
3395                                                               labels, values, G_N_ELEMENTS(labels),
3396                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3397         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3398         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3399         }
3401         {
3402         /* Tremor */
3403             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3404         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3405         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3406                                                               _("Stroke Tremor"), _("Tremor:"),
3407                                                               _("Increase to make strokes rugged and trembling"),
3408                                                               "tools.calligraphic", "tremor", 0.0,
3409                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3410                                                               0.0, 1.0, 0.01, 0.1,
3411                                                               labels, values, G_N_ELEMENTS(labels),
3412                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3414         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3415         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3416         }
3418         {
3419         /* Wiggle */
3420         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3421         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3422         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3423                                                               _("Pen Wiggle"), _("Wiggle:"),
3424                                                               _("Increase to make the pen waver and wiggle"),
3425                                                               "tools.calligraphic", "wiggle", 0.0,
3426                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3427                                                               0.0, 1.0, 0.01, 0.1,
3428                                                               labels, values, G_N_ELEMENTS(labels),
3429                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3430         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3431         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3432         }
3434         {
3435         /* Mass */
3436             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3437         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3438         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3439                                                               _("Pen Mass"), _("Mass:"),
3440                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3441                                                               "tools.calligraphic", "mass", 0.02,
3442                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3443                                                               0.0, 1.0, 0.01, 0.1,
3444                                                               labels, values, G_N_ELEMENTS(labels),
3445                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3446         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3447         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3448         }
3451         /* Trace Background button */
3452         {
3453             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3454                                                           _("Trace Background"),
3455                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3456                                                           "trace_background",
3457                                                           Inkscape::ICON_SIZE_DECORATION );
3458             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3459             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3460             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3461         }
3463         /* Use Pressure button */
3464         {
3465             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3466                                                           _("Pressure"),
3467                                                           _("Use the pressure of the input device to alter the width of the pen"),
3468                                                           "use_pressure",
3469                                                           Inkscape::ICON_SIZE_DECORATION );
3470             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3471             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3472             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3473         }
3475         /* Use Tilt button */
3476         {
3477             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3478                                                           _("Tilt"),
3479                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3480                                                           "use_tilt",
3481                                                           Inkscape::ICON_SIZE_DECORATION );
3482             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3483             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3484             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3485             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3486         }
3488         /* Reset */
3489         {
3490             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3491                                              _("Defaults"),
3492                                              _("Reset all parameters to defaults"),
3493                                              GTK_STOCK_CLEAR );
3494             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3495             gtk_action_group_add_action( mainActions, act );
3496             gtk_action_set_sensitive( act, TRUE );
3497         }
3498     }
3502 //########################
3503 //##    Circle / Arc    ##
3504 //########################
3506 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3508     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3509     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3511     if (v1 == 0 && v2 == 0) {
3512         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3513             gtk_action_set_sensitive( ocb, FALSE );
3514             gtk_action_set_sensitive( make_whole, FALSE );
3515         }
3516     } else {
3517         gtk_action_set_sensitive( ocb, TRUE );
3518         gtk_action_set_sensitive( make_whole, TRUE );
3519     }
3522 static void
3523 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3525     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3527     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3528         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3529     }
3531     // quit if run by the attr_changed listener
3532     if (g_object_get_data( tbl, "freeze" )) {
3533         return;
3534     }
3536     // in turn, prevent listener from responding
3537     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3539     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3541     bool modmade = false;
3542     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3543          items != NULL;
3544          items = items->next)
3545     {
3546         SPItem *item = SP_ITEM(items->data);
3548         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3550             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3551             SPArc *arc = SP_ARC(item);
3553             if (!strcmp(value_name, "start"))
3554                 ge->start = (adj->value * M_PI)/ 180;
3555             else
3556                 ge->end = (adj->value * M_PI)/ 180;
3558             sp_genericellipse_normalize(ge);
3559             ((SPObject *)arc)->updateRepr();
3560             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3562             modmade = true;
3563         }
3564     }
3566     g_free(namespaced_name);
3568     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3570     sp_arctb_sensitivize( tbl, adj->value, other->value );
3572     if (modmade) {
3573         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3574                                    _("Arc: Change start/end"));
3575     }
3577     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3581 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3583     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3586 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3588     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3591 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3593     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3594     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3595         if ( ege_select_one_action_get_active( act ) != 0 ) {
3596             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3597         } else {
3598             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3599         }
3600     }
3602     // quit if run by the attr_changed listener
3603     if (g_object_get_data( tbl, "freeze" )) {
3604         return;
3605     }
3607     // in turn, prevent listener from responding
3608     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3610     bool modmade = false;
3612     if ( ege_select_one_action_get_active(act) != 0 ) {
3613         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3614              items != NULL;
3615              items = items->next)
3616         {
3617             if (SP_IS_ARC((SPItem *) items->data)) {
3618                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3619                 repr->setAttribute("sodipodi:open", "true");
3620                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3621                 modmade = true;
3622             }
3623         }
3624     } else {
3625         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3626              items != NULL;
3627              items = items->next)
3628         {
3629             if (SP_IS_ARC((SPItem *) items->data))    {
3630                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3631                 repr->setAttribute("sodipodi:open", NULL);
3632                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3633                 modmade = true;
3634             }
3635         }
3636     }
3638     if (modmade) {
3639         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3640                                    _("Arc: Change open/closed"));
3641     }
3643     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3646 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3648     GtkAdjustment *adj;
3649     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3650     gtk_adjustment_set_value(adj, 0.0);
3651     gtk_adjustment_value_changed(adj);
3653     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3654     gtk_adjustment_set_value(adj, 0.0);
3655     gtk_adjustment_value_changed(adj);
3657     spinbutton_defocus( GTK_OBJECT(obj) );
3660 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3661                                       gchar const */*old_value*/, gchar const */*new_value*/,
3662                                       bool /*is_interactive*/, gpointer data)
3664     GObject *tbl = G_OBJECT(data);
3666     // quit if run by the _changed callbacks
3667     if (g_object_get_data( tbl, "freeze" )) {
3668         return;
3669     }
3671     // in turn, prevent callbacks from responding
3672     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3674     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3675     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3677     GtkAdjustment *adj1,*adj2;
3678     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3679     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3680     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3681     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3683     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3685     char const *openstr = NULL;
3686     openstr = repr->attribute("sodipodi:open");
3687     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3689     if (openstr) {
3690         ege_select_one_action_set_active( ocb, 1 );
3691     } else {
3692         ege_select_one_action_set_active( ocb, 0 );
3693     }
3695     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3698 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3699     NULL, /* child_added */
3700     NULL, /* child_removed */
3701     arc_tb_event_attr_changed,
3702     NULL, /* content_changed */
3703     NULL  /* order_changed */
3704 };
3707 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3709     int n_selected = 0;
3710     Inkscape::XML::Node *repr = NULL;
3712     purge_repr_listener( tbl, tbl );
3714     for (GSList const *items = selection->itemList();
3715          items != NULL;
3716          items = items->next)
3717     {
3718         if (SP_IS_ARC((SPItem *) items->data)) {
3719             n_selected++;
3720             repr = SP_OBJECT_REPR((SPItem *) items->data);
3721         }
3722     }
3724     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3726     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3727     if (n_selected == 0) {
3728         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3729     } else if (n_selected == 1) {
3730         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3731         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3733         if (repr) {
3734             g_object_set_data( tbl, "repr", repr );
3735             Inkscape::GC::anchor(repr);
3736             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3737             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3738         }
3739     } else {
3740         // FIXME: implement averaging of all parameters for multiple selected
3741         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3742         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3743         sp_arctb_sensitivize( tbl, 1, 0 );
3744     }
3748 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3750     EgeAdjustmentAction* eact = 0;
3753     {
3754         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3755         ege_output_action_set_use_markup( act, TRUE );
3756         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3757         g_object_set_data( holder, "mode_action", act );
3758     }
3760     /* Start */
3761     {
3762         eact = create_adjustment_action( "ArcStartAction",
3763                                          _("Start"), _("Start:"),
3764                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3765                                          "tools.shapes.arc", "start", 0.0,
3766                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3767                                          -360.0, 360.0, 1.0, 10.0,
3768                                          0, 0, 0,
3769                                          sp_arctb_start_value_changed);
3770         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3771     }
3773     /* End */
3774     {
3775         eact = create_adjustment_action( "ArcEndAction",
3776                                          _("End"), _("End:"),
3777                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3778                                          "tools.shapes.arc", "end", 0.0,
3779                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3780                                          -360.0, 360.0, 1.0, 10.0,
3781                                          0, 0, 0,
3782                                          sp_arctb_end_value_changed);
3783         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3784     }
3786     /* Segments / Pie checkbox */
3787     {
3788         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3790         GtkTreeIter iter;
3791         gtk_list_store_append( model, &iter );
3792         gtk_list_store_set( model, &iter,
3793                             0, _("Closed arc"),
3794                             1, _("Switch to segment (closed shape with two radii)"),
3795                             2, "circle_closed_arc",
3796                             -1 );
3798         gtk_list_store_append( model, &iter );
3799         gtk_list_store_set( model, &iter,
3800                             0, _("Open Arc"),
3801                             1, _("Switch to arc (unclosed shape)"),
3802                             2, "circle_open_arc",
3803                             -1 );
3805         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3806         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3807         g_object_set_data( holder, "open_action", act );
3809         ege_select_one_action_set_appearance( act, "full" );
3810         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3811         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3812         ege_select_one_action_set_icon_column( act, 2 );
3813         ege_select_one_action_set_tooltip_column( act, 1  );
3815         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3816         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3817         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3818         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3819     }
3821     /* Make Whole */
3822     {
3823         InkAction* inky = ink_action_new( "ArcResetAction",
3824                                           _("Make whole"),
3825                                           _("Make the shape a whole ellipse, not arc or segment"),
3826                                           "reset_circle",
3827                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3828         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3829         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3830         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3831         g_object_set_data( holder, "make_whole", inky );
3832     }
3834     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3835     // sensitivize make whole and open checkbox
3836     {
3837         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3838         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3839         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3840     }
3843     sigc::connection *connection = new sigc::connection(
3844         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3845         );
3846     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3847     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3853 // toggle button callbacks and updaters
3855 //########################
3856 //##      Dropper       ##
3857 //########################
3859 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3860     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3861     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3862     if ( set_action ) {
3863         if ( gtk_toggle_action_get_active( act ) ) {
3864             gtk_action_set_sensitive( set_action, TRUE );
3865         } else {
3866             gtk_action_set_sensitive( set_action, FALSE );
3867         }
3868     }
3870     spinbutton_defocus(GTK_OBJECT(tbl));
3873 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3874     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3875     spinbutton_defocus(GTK_OBJECT(tbl));
3879 /**
3880  * Dropper auxiliary toolbar construction and setup.
3881  *
3882  * TODO: Would like to add swatch of current color.
3883  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3884  *       can drag and drop places. Will provide a nice mixing palette.
3885  */
3886 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3888     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3890     {
3891         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3892                                                       _("Pick alpha"),
3893                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3894                                                       "color_alpha_get",
3895                                                       Inkscape::ICON_SIZE_DECORATION );
3896         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3897         g_object_set_data( holder, "pick_action", act );
3898         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3899         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3900     }
3902     {
3903         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3904                                                       _("Set alpha"),
3905                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3906                                                       "color_alpha_set",
3907                                                       Inkscape::ICON_SIZE_DECORATION );
3908         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3909         g_object_set_data( holder, "set_action", act );
3910         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3911         // make sure it's disabled if we're not picking alpha
3912         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3913         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3914     }
3918 //########################
3919 //##    Text Toolbox    ##
3920 //########################
3921 /*
3922 static void
3923 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3925     //Call back for letter sizing spinbutton
3928 static void
3929 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3931     //Call back for line height spinbutton
3934 static void
3935 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3937     //Call back for horizontal kerning spinbutton
3940 static void
3941 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3943     //Call back for vertical kerning spinbutton
3946 static void
3947 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3949     //Call back for letter rotation spinbutton
3950 }*/
3952 namespace {
3954 bool visible = false;
3956 void
3957 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3959     SPStyle *query =
3960         sp_style_new (SP_ACTIVE_DOCUMENT);
3961     
3962     int result_fontspec =
3963         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3965     int result_family =
3966         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3968     int result_style =
3969         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3971     int result_numbers =
3972         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3974     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3976     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3977     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3978     {
3979         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3981         if (repr)
3982         {
3983             sp_style_read_from_repr (query, repr);
3984         }
3985         else
3986         {
3987             return;
3988         }
3989     }
3991     if (query->text)
3992     {
3993         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3994             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3995             gtk_entry_set_text (GTK_ENTRY (entry), "");
3997         } else if (query->text->font_specification.value || query->text->font_family.value) {
3999             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4000             
4001             // Get the font that corresponds 
4002             Glib::ustring familyName;
4003             
4004             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4005             if (font) {
4006                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4007                 font->Unref();
4008                 font = NULL;
4009             }
4010             
4011             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4013             Gtk::TreePath path;
4014             try {
4015                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4016             } catch (...) {
4017                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4018                 return;
4019             }
4021             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4022             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4024             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4026             gtk_tree_selection_select_path (tselection, path.gobj());
4027             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4029             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4030         }
4032         //Size
4033         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4034         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4035         g_object_set_data (tbl, "size-block", gpointer(1));
4036         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4037         g_object_set_data (tbl, "size-block", gpointer(0));
4038         free (str);
4040         //Anchor
4041         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4042         {
4043             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4044             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4045             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4046             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4047         }
4048         else
4049         {
4050             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4051             {
4052                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4053                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4054                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4055                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4056             }
4057             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4058             {
4059                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4060                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4061                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4062                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4063             }
4064             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4065             {
4066                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4067                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4068                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4069                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4070             }
4071         }
4073         //Style
4074         {
4075             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4077             gboolean active = gtk_toggle_button_get_active (button);
4078             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4080             if (active != check)
4081             {
4082                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4083                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4084                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4085             }
4086         }
4088         {
4089             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4091             gboolean active = gtk_toggle_button_get_active (button);
4092             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4094             if (active != check)
4095             {
4096                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4097                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4098                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4099             }
4100         }
4102         //Orientation
4103         //locking both buttons, changing one affect all group (both)
4104         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4105         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4107         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4108         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4110         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4111         {
4112             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4113         }
4114         else
4115         {
4116             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4117         }
4118         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4119         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4120     }
4122     sp_style_unref(query);
4125 void
4126 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4128     sp_text_toolbox_selection_changed (selection, tbl);
4131 void
4132 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4134     sp_text_toolbox_selection_changed (NULL, tbl);
4137 void
4138 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4139                                 GObject             *tbl)
4141     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4142     GtkTreeModel *model = 0;
4143     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4144     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4145     GtkTreeIter   iter;
4146     char         *family = 0;
4148     (void)popdown;
4150     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4151     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4153     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4154         return;
4155     }
4157     gtk_tree_model_get (model, &iter, 0, &family, -1);
4159     if (g_object_get_data (G_OBJECT (selection), "block"))
4160     {
4161         gtk_entry_set_text (GTK_ENTRY (entry), family);
4162         return;
4163     }
4165     gtk_entry_set_text (GTK_ENTRY (entry), family);
4167     SPStyle *query =
4168         sp_style_new (SP_ACTIVE_DOCUMENT);
4170     int result_fontspec =
4171         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4172     
4173     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4174     
4175     SPCSSAttr *css = sp_repr_css_attr_new ();
4176     
4177     
4178     // First try to get the font spec from the stored value
4179     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4180     
4181     if (fontSpec.empty()) {
4182         // Construct a new font specification if it does not yet exist
4183         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4184         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4185         fontFromStyle->Unref();
4186     }
4187     
4188     if (!fontSpec.empty()) {
4189         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4190         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4191             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4192             if (font) {
4193                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4194                 
4195                 // Set all the these just in case they were altered when finding the best
4196                 // match for the new family and old style...
4197                 
4198                 gchar c[256];
4199                 
4200                 font->Family(c, 256);
4201                 sp_repr_css_set_property (css, "font-family", c);
4202                 
4203                 font->Attribute( "weight", c, 256);
4204                 sp_repr_css_set_property (css, "font-weight", c);
4205                 
4206                 font->Attribute("style", c, 256);
4207                 sp_repr_css_set_property (css, "font-style", c);
4208                 
4209                 font->Attribute("stretch", c, 256);
4210                 sp_repr_css_set_property (css, "font-stretch", c);
4211                 
4212                 font->Attribute("variant", c, 256);
4213                 sp_repr_css_set_property (css, "font-variant", c);
4214                 
4215                 font->Unref();
4216             }
4217         }
4218     }
4220     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4221     if (result_fontspec == QUERY_STYLE_NOTHING)
4222     {
4223         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4224         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4225     }
4226     else
4227     {
4228         sp_desktop_set_style (desktop, css, true, true);
4229     }
4231     sp_style_unref(query);
4233     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4234                                    _("Text: Change font family"));
4235     sp_repr_css_attr_unref (css);
4236     free (family);
4237     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4239     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4242 void
4243 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4244                                        GObject      *tbl)
4246     const char *family = gtk_entry_get_text (entry);
4248     try {
4249         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4250         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4251         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4252         gtk_tree_selection_select_path (selection, path.gobj());
4253         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4254         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4255     } catch (...) {
4256         if (family && strlen (family))
4257         {
4258             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4259         }
4260     }
4263 void
4264 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4265                                    gpointer          data)
4267     if (g_object_get_data (G_OBJECT (button), "block")) return;
4268     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4269     int prop = GPOINTER_TO_INT(data);
4271     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4272     SPCSSAttr *css = sp_repr_css_attr_new ();
4274     switch (prop)
4275     {
4276         case 0:
4277         {
4278             sp_repr_css_set_property (css, "text-anchor", "start");
4279             sp_repr_css_set_property (css, "text-align", "start");
4280             break;
4281         }
4282         case 1:
4283         {
4284             sp_repr_css_set_property (css, "text-anchor", "middle");
4285             sp_repr_css_set_property (css, "text-align", "center");
4286             break;
4287         }
4289         case 2:
4290         {
4291             sp_repr_css_set_property (css, "text-anchor", "end");
4292             sp_repr_css_set_property (css, "text-align", "end");
4293             break;
4294         }
4296         case 3:
4297         {
4298             sp_repr_css_set_property (css, "text-anchor", "start");
4299             sp_repr_css_set_property (css, "text-align", "justify");
4300             break;
4301         }
4302     }
4304     SPStyle *query =
4305         sp_style_new (SP_ACTIVE_DOCUMENT);
4306     int result_numbers =
4307         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4309     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4310     if (result_numbers == QUERY_STYLE_NOTHING)
4311     {
4312         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4313     }
4315     sp_style_unref(query);
4317     sp_desktop_set_style (desktop, css, true, true);
4318     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4319                                    _("Text: Change alignment"));
4320     sp_repr_css_attr_unref (css);
4322     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4325 void
4326 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4327                                gpointer          data)
4329     if (g_object_get_data (G_OBJECT (button), "block")) return;
4331     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4332     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4333     int          prop       = GPOINTER_TO_INT(data);
4334     bool         active     = gtk_toggle_button_get_active (button);
4336     SPStyle *query =
4337         sp_style_new (SP_ACTIVE_DOCUMENT);
4338     
4339     int result_fontspec =
4340         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4342     int result_family =
4343         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4345     int result_style =
4346         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4348     int result_numbers =
4349         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4350     
4351     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4352     Glib::ustring newFontSpec = "";
4353     
4354     if (fontSpec.empty()) {
4355         // Construct a new font specification if it does not yet exist
4356         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4357         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4358         fontFromStyle->Unref();
4359     }
4360     
4361     switch (prop)
4362     {
4363         case 0:
4364         {
4365             if (!fontSpec.empty()) {
4366                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4367             }
4368             if (fontSpec != newFontSpec) {
4369                 // Don't even set the bold if the font didn't exist on the system
4370                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4371             }
4372             break;
4373         }
4375         case 1:
4376         {
4377             if (!fontSpec.empty()) {
4378                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4379             }
4380             if (fontSpec != newFontSpec) {
4381                 // Don't even set the italic if the font didn't exist on the system
4382                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4383             }
4384             break;
4385         }
4386     }
4388     if (!newFontSpec.empty()) {
4389         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str()); 
4390     }
4392     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4393     if (result_fontspec == QUERY_STYLE_NOTHING)
4394     {
4395         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4396     }
4398     sp_style_unref(query);
4400     sp_desktop_set_style (desktop, css, true, true);
4401     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4402                                    _("Text: Change font style"));
4403     sp_repr_css_attr_unref (css);
4405     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4408 void
4409 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4410                                      gpointer         data)
4412     if (g_object_get_data (G_OBJECT (button), "block")) {
4413         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4414         return;
4415     }
4417     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4418     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4419     int          prop       = GPOINTER_TO_INT(data);
4421     switch (prop)
4422     {
4423         case 0:
4424         {
4425             sp_repr_css_set_property (css, "writing-mode", "lr");
4426             break;
4427         }
4429         case 1:
4430         {
4431             sp_repr_css_set_property (css, "writing-mode", "tb");
4432             break;
4433         }
4434     }
4436     SPStyle *query =
4437         sp_style_new (SP_ACTIVE_DOCUMENT);
4438     int result_numbers =
4439         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4441     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4442     if (result_numbers == QUERY_STYLE_NOTHING)
4443     {
4444         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4445     }
4447     sp_desktop_set_style (desktop, css, true, true);
4448     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4449                                    _("Text: Change orientation"));
4450     sp_repr_css_attr_unref (css);
4452     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4455 gboolean
4456 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4458     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4459     if (!desktop) return FALSE;
4461     switch (get_group0_keyval (event)) {
4462         case GDK_Escape: // defocus
4463             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4464             sp_text_toolbox_selection_changed (NULL, tbl); // update
4465             return TRUE; // I consumed the event
4466             break;
4467     }
4468     return FALSE;
4471 gboolean
4472 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4474     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4475     if (!desktop) return FALSE;
4477     switch (get_group0_keyval (event)) {
4478         case GDK_KP_Enter:
4479         case GDK_Return:
4480         case GDK_Escape: // defocus
4481             gtk_widget_hide (w);
4482             visible = false;
4483             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4484             return TRUE; // I consumed the event
4485             break;
4486     }
4487     return FALSE;
4491 void
4492 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4493                                GObject     *tbl)
4495     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4497     if (g_object_get_data (tbl, "size-block")) return;
4499     // If this is not from selecting a size in the list (in which case get_active will give the
4500     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4501     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4502     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4503     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4504         return;
4506     gchar *endptr;
4507     gdouble value = -1;
4508     char *text = gtk_combo_box_get_active_text (cbox);
4509     if (text) {
4510         value = g_strtod (text, &endptr);
4511         if (endptr == text) // conversion failed, non-numeric input
4512             value = -1;
4513         free (text);
4514     }
4515     if (value <= 0) {
4516         return; // could not parse value 
4517     }
4519     SPCSSAttr *css = sp_repr_css_attr_new ();
4520     Inkscape::CSSOStringStream osfs;
4521     osfs << value;
4522     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4524     SPStyle *query =
4525         sp_style_new (SP_ACTIVE_DOCUMENT);
4526     int result_numbers =
4527         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4529     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4530     if (result_numbers == QUERY_STYLE_NOTHING)
4531     {
4532         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4533     }
4535     sp_style_unref(query);
4537     sp_desktop_set_style (desktop, css, true, true);
4538     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4539                                    _("Text: Change font size"));
4540     sp_repr_css_attr_unref (css);
4542     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4545 gboolean
4546 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus *event, GObject *tbl)
4548     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4549     if (!desktop) return FALSE;
4551     if (!g_object_get_data (tbl, "esc-pressed")) {
4552         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4553         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4554         sp_text_toolbox_size_changed (cbox, tbl);
4555         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4556     }
4557     return FALSE; // I consumed the event
4561 gboolean
4562 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4564     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4565     if (!desktop) return FALSE;
4567     switch (get_group0_keyval (event)) {
4568         case GDK_Escape: // defocus
4569             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4570             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4571             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4572             return TRUE; // I consumed the event
4573             break;
4574         case GDK_Return: // defocus
4575         case GDK_KP_Enter:
4576             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4577             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4578             sp_text_toolbox_size_changed (cbox, tbl);
4579             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4580             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4581             return TRUE; // I consumed the event
4582             break;
4583     }
4584     return FALSE;
4587 void
4588 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4589                                          GObject            *tbl)
4591     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4592     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4593     int x, y;
4595     if (!visible)
4596     {
4597         gdk_window_get_origin (widget->window, &x, &y);
4598         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4599         gtk_widget_show_all (popdown);
4601         gdk_pointer_grab (widget->window, TRUE,
4602                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4603                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4604                                         GDK_POINTER_MOTION_MASK),
4605                           NULL, NULL, GDK_CURRENT_TIME);
4607         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4609         visible = true;
4610     }
4611     else
4612     {
4613         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4614         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4615         gtk_widget_hide (popdown);
4616         visible = false;
4617     }
4620 gboolean
4621 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4622                                  GdkEventFocus    */*event*/,
4623                                  GObject          */*tbl*/)
4625     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4626     return FALSE;
4629 gboolean
4630 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4631                                    GdkEventFocus    */*event*/,
4632                                    GObject          */*tbl*/)
4634     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4636     gtk_widget_hide (popdown);
4637     visible = false;
4638     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4639     return TRUE;
4642 void
4643 cell_data_func  (GtkTreeViewColumn */*column*/,
4644                  GtkCellRenderer   *cell,
4645                  GtkTreeModel      *tree_model,
4646                  GtkTreeIter       *iter,
4647                  gpointer           /*data*/)
4649     char        *family,
4650         *family_escaped,
4651         *sample_escaped;
4653     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4655     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4657     family_escaped = g_markup_escape_text (family, -1);
4658     sample_escaped = g_markup_escape_text (sample, -1);
4660     std::stringstream markup;
4661     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4662     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4664     free (family);
4665     free (family_escaped);
4666     free (sample_escaped);
4669 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4670     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4671     if (completion) {
4672         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4673         g_object_unref (completion);
4674     }
4677 GtkWidget*
4678 sp_text_toolbox_new (SPDesktop *desktop)
4680     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4682     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4683     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4685     GtkTooltips *tt = gtk_tooltips_new();
4686     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4688     ////////////Family
4689     //Window
4690     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4691     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4693     //Entry
4694     GtkWidget           *entry = gtk_entry_new ();
4695     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4696     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4697     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4698     gtk_entry_completion_set_text_column (completion, 0);
4699     gtk_entry_completion_set_minimum_key_length (completion, 1);
4700     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4701     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4702     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4703     aux_toolbox_space (tbl, 1);
4704     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4705     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4707     //Button
4708     GtkWidget   *button = gtk_button_new ();
4709     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4710     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4712     //Popdown
4713     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4714     GtkWidget           *treeview = gtk_tree_view_new ();
4716     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4717     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4718     gtk_tree_view_column_pack_start (column, cell, FALSE);
4719     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4720     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4721     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4723     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4724     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4725     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4727     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4729     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4730     gtk_container_add (GTK_CONTAINER (sw), treeview);
4732     gtk_container_add (GTK_CONTAINER (window), sw);
4733     gtk_widget_set_size_request (window, 300, 450);
4735     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4736     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4737     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4739     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4741     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4742     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4744     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4745     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4747     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4748     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4749     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4750     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4751     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4753     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4754     aux_toolbox_space (tbl, 1);
4755     GtkWidget *box = gtk_event_box_new ();
4756     gtk_container_add (GTK_CONTAINER (box), image);
4757     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4758     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4759     GtkTooltips *tooltips = gtk_tooltips_new ();
4760     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4761     gtk_widget_hide (GTK_WIDGET (box));
4762     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4764     ////////////Size
4765     const char *sizes[] = {
4766         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4767         "16", "18", "20", "22", "24", "28",
4768         "32", "36", "40", "48", "56", "64", "72", "144"
4769     };
4771     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4772     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4773     gtk_widget_set_size_request (cbox, 80, -1);
4774     aux_toolbox_space (tbl, 1);
4775     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4776     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4777     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4778     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4779     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4781     //spacer
4782     aux_toolbox_space (tbl, 4);
4783     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4785     ////////////Text anchor
4786     GtkWidget *group   = gtk_radio_button_new (NULL);
4787     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4788     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4790     // left
4791     GtkWidget *rbutton = group;
4792     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4793     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4794     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4796     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4797     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4798     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4799     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4801     // center
4802     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4803     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4804     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4805     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4807     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4808     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4809     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4810     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4812     // right
4813     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4814     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4815     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4816     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4818     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4819     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4820     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4821     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4823     // fill
4824     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4825     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4826     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4827     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4829     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4830     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4831     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4832     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4834     aux_toolbox_space (tbl, 1);
4835     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4837     //spacer
4838     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4840     ////////////Text style
4841     row = gtk_hbox_new (FALSE, 4);
4843     // bold
4844     rbutton = gtk_toggle_button_new ();
4845     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4846     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4847     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4848     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4850     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4851     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4852     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4854     // italic
4855     rbutton = gtk_toggle_button_new ();
4856     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4857     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4858     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4859     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4861     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4862     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4863     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4865     aux_toolbox_space (tbl, 1);
4866     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4868     //spacer
4869     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4871     ////////////Text orientation
4872     group   = gtk_radio_button_new (NULL);
4873     row     = gtk_hbox_new (FALSE, 4);
4874     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4876     // horizontal
4877     rbutton = group;
4878     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4879     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4880     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4881     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4883     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4884     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4885     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4887     // vertical
4888     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4889     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4890     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4891     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4892     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4894     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4895     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4896     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4897     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4900     //watch selection
4901     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4903     sigc::connection *c_selection_changed =
4904         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4905                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4906     pool->add_connection ("selection-changed", c_selection_changed);
4908     sigc::connection *c_selection_modified =
4909         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4910                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4911     pool->add_connection ("selection-modified", c_selection_modified);
4913     sigc::connection *c_subselection_changed =
4914         new sigc::connection (desktop->connectToolSubselectionChanged
4915                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4916     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4918     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4921     gtk_widget_show_all (tbl);
4922     return tbl;
4924 } // end of sp_text_toolbox_new()
4926 }//<unnamed> namespace
4929 //#########################
4930 //##      Connector      ##
4931 //#########################
4933 static void sp_connector_path_set_avoid(void)
4935     cc_selection_set_avoid(true);
4939 static void sp_connector_path_set_ignore(void)
4941     cc_selection_set_avoid(false);
4946 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4948     // quit if run by the _changed callbacks
4949     if (g_object_get_data( tbl, "freeze" )) {
4950         return;
4951     }
4953     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4954     SPDocument *doc = sp_desktop_document(desktop);
4956     if (!sp_document_get_undo_sensitive(doc))
4957     {
4958         return;
4959     }
4961     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4963     if ( repr->attribute("inkscape:connector-spacing") ) {
4964         gdouble priorValue = gtk_adjustment_get_value(adj);
4965         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4966         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4967             return;
4968         }
4969     } else if ( adj->value == defaultConnSpacing ) {
4970         return;
4971     }
4973     // in turn, prevent callbacks from responding
4974     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4976     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4977     SP_OBJECT(desktop->namedview)->updateRepr();
4979     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4980     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4981         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4982         NR::Matrix m = NR::identity();
4983         avoid_item_move(&m, item);
4984     }
4986     if (items) {
4987         g_slist_free(items);
4988     }
4990     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4991             _("Change connector spacing"));
4993     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4995     spinbutton_defocus(GTK_OBJECT(tbl));
4998 static void sp_connector_graph_layout(void)
5000     if (!SP_ACTIVE_DESKTOP) return;
5002     // hack for clones, see comment in align-and-distribute.cpp
5003     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5004     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5006     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5008     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5010     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5013 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5015     if ( gtk_toggle_action_get_active( act ) ) {
5016         prefs_set_string_attribute("tools.connector", "directedlayout",
5017                 "true");
5018     } else {
5019         prefs_set_string_attribute("tools.connector", "directedlayout",
5020                 "false");
5021     }
5024 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5026     if ( gtk_toggle_action_get_active( act ) ) {
5027         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5028                 "true");
5029     } else {
5030         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5031                 "false");
5032     }
5036 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5038     prefs_set_double_attribute("tools.connector", "length", adj->value);
5039     spinbutton_defocus(GTK_OBJECT(tbl));
5042 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5043                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5044                                             bool /*is_interactive*/, gpointer data)
5046     GtkWidget *tbl = GTK_WIDGET(data);
5048     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5049         return;
5050     }
5051     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5052         return;
5053     }
5055     GtkAdjustment *adj = (GtkAdjustment*)
5056             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5057     gdouble spacing = defaultConnSpacing;
5058     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5060     gtk_adjustment_set_value(adj, spacing);
5064 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5065     NULL, /* child_added */
5066     NULL, /* child_removed */
5067     connector_tb_event_attr_changed,
5068     NULL, /* content_changed */
5069     NULL  /* order_changed */
5070 };
5073 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5075     {
5076         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5077                                           _("Avoid"),
5078                                           _("Make connectors avoid selected objects"),
5079                                           "connector_avoid",
5080                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5081         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5082         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5083     }
5085     {
5086         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5087                                           _("Ignore"),
5088                                           _("Make connectors ignore selected objects"),
5089                                           "connector_ignore",
5090                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5091         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5092         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5093     }
5095     EgeAdjustmentAction* eact = 0;
5097     // Spacing spinbox
5098     eact = create_adjustment_action( "ConnectorSpacingAction",
5099                                      _("Connector Spacing"), _("Spacing:"),
5100                                      _("The amount of space left around objects by auto-routing connectors"),
5101                                      "tools.connector", "spacing", defaultConnSpacing,
5102                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5103                                      0, 100, 1.0, 10.0,
5104                                      0, 0, 0,
5105                                      connector_spacing_changed, 1, 0 );
5106     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5108     // Graph (connector network) layout
5109     {
5110         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5111                                           _("Graph"),
5112                                           _("Nicely arrange selected connector network"),
5113                                           "graph_layout",
5114                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5115         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5116         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5117     }
5119     // Default connector length spinbox
5120     eact = create_adjustment_action( "ConnectorLengthAction",
5121                                      _("Connector Length"), _("Length:"),
5122                                      _("Ideal length for connectors when layout is applied"),
5123                                      "tools.connector", "length", 100,
5124                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5125                                      10, 1000, 10.0, 100.0,
5126                                      0, 0, 0,
5127                                      connector_length_changed, 1, 0 );
5128     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5131     // Directed edges toggle button
5132     {
5133         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5134                                                       _("Downwards"),
5135                                                       _("Make connectors with end-markers (arrows) point downwards"),
5136                                                       "directed_graph",
5137                                                       Inkscape::ICON_SIZE_DECORATION );
5138         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5140         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5141         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5142                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5144         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5145     }
5147     // Avoid overlaps toggle button
5148     {
5149         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5150                                                       _("Remove overlaps"),
5151                                                       _("Do not allow overlapping shapes"),
5152                                                       "remove_overlaps",
5153                                                       Inkscape::ICON_SIZE_DECORATION );
5154         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5156         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5157         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5158                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5160         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5161     }
5163     // Code to watch for changes to the connector-spacing attribute in
5164     // the XML.
5165     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5166     g_assert(repr != NULL);
5168     purge_repr_listener( holder, holder );
5170     if (repr) {
5171         g_object_set_data( holder, "repr", repr );
5172         Inkscape::GC::anchor(repr);
5173         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5174         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5175     }
5176 } // end of sp_connector_toolbox_prep()
5179 //#########################
5180 //##     Paintbucket     ##
5181 //#########################
5183 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5185     gint channels = ege_select_one_action_get_active( act );
5186     flood_channels_set_channels( channels );
5189 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5191     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5194 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5196     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5199 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5201     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5202     SPUnit const *unit = tracker->getActiveUnit();
5204     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5206     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5209 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5211     // FIXME: make defaults settable via Inkscape Options
5212     struct KeyValue {
5213         char const *key;
5214         double value;
5215     } const key_values[] = {
5216         {"threshold", 15},
5217         {"offset", 0.0}
5218     };
5220     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5221         KeyValue const &kv = key_values[i];
5222         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5223         if ( adj ) {
5224             gtk_adjustment_set_value(adj, kv.value);
5225         }
5226     }
5228     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5229     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5230     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5231     ege_select_one_action_set_active( autogap_action, 0 );
5234 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5236     EgeAdjustmentAction* eact = 0;
5238     {
5239         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5241         GList* items = 0;
5242         gint count = 0;
5243         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5244         {
5245             GtkTreeIter iter;
5246             gtk_list_store_append( model, &iter );
5247             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5248             count++;
5249         }
5250         g_list_free( items );
5251         items = 0;
5252         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5253         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5254         ege_select_one_action_set_appearance( act1, "compact" );
5255         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5256         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5257         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5258         g_object_set_data( holder, "channels_action", act1 );
5259     }
5261     // Spacing spinbox
5262     {
5263         eact = create_adjustment_action(
5264             "ThresholdAction",
5265             _("Fill Threshold"), _("Threshold:"),
5266             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5267             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5268             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5269             0, 0, 0,
5270             paintbucket_threshold_changed, 1, 0 );
5272         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5273     }
5275     // Create the units menu.
5276     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5277     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5278     g_object_set_data( holder, "tracker", tracker );
5279     {
5280         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5281         gtk_action_group_add_action( mainActions, act );
5282     }
5284     // Offset spinbox
5285     {
5286         eact = create_adjustment_action(
5287             "OffsetAction",
5288             _("Grow/shrink by"), _("Grow/shrink by:"),
5289             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5290             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5291             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5292             0, 0, 0,
5293             paintbucket_offset_changed, 1, 2);
5294         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5296         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5297     }
5299     /* Auto Gap */
5300     {
5301         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5303         GList* items = 0;
5304         gint count = 0;
5305         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5306         {
5307             GtkTreeIter iter;
5308             gtk_list_store_append( model, &iter );
5309             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5310             count++;
5311         }
5312         g_list_free( items );
5313         items = 0;
5314         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5315         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5316         ege_select_one_action_set_appearance( act2, "compact" );
5317         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5318         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5319         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5320         g_object_set_data( holder, "autogap_action", act2 );
5321     }
5323     /* Reset */
5324     {
5325         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5326                                           _("Defaults"),
5327                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5328                                           GTK_STOCK_CLEAR );
5329         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5330         gtk_action_group_add_action( mainActions, act );
5331         gtk_action_set_sensitive( act, TRUE );
5332     }
5336 /*
5337   Local Variables:
5338   mode:c++
5339   c-file-style:"stroustrup"
5340   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5341   indent-tabs-mode:nil
5342   fill-column:99
5343   End:
5344 */
5345 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :