Code

c2c7ff8528566aeb7de8e569bc5c6923b5ee1c48
[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 "../connection-pool.h"
80 #include "../prefs-utils.h"
81 #include "../inkscape-stock.h"
82 #include "icon.h"
83 #include "graphlayout/graphlayout.h"
85 #include "mod360.h"
87 #include "toolbox.h"
89 #include "flood-context.h"
91 #include "ink-action.h"
92 #include "ege-adjustment-action.h"
93 #include "ege-output-action.h"
94 #include "ege-select-one-action.h"
95 #include "helper/unit-tracker.h"
97 using Inkscape::UnitTracker;
99 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
100 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
102 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
103 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
115 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
121 static struct {
122     gchar const *type_name;
123     gchar const *data_name;
124     sp_verb_t verb;
125     sp_verb_t doubleclick_verb;
126 } const tools[] = {
127     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
128     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
129     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
130     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
131     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
132 //    { "SP3DBoxContext",    "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
133     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
134     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
135     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
136     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
137     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
138     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
139     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
140     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
141     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
142     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
143     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
144     { NULL, NULL, 0, 0 }
145 };
147 static struct {
148     gchar const *type_name;
149     gchar const *data_name;
150     GtkWidget *(*create_func)(SPDesktop *desktop);
151     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
152     gchar const *ui_name;
153     gint swatch_verb_id;
154     gchar const *swatch_tool;
155     gchar const *swatch_tip;
156 } const aux_toolboxes[] = {
157     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
158       SP_VERB_INVALID, 0, 0},
159     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
160       SP_VERB_INVALID, 0, 0},
161     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
162       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
163     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
164       SP_VERB_INVALID, 0, 0},
165     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
166       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
167     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
168       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
169     { "SP3DBoxContext",  "3dbox_toolbox",  0, sp_3dbox_toolbox_prep,             "3DBoxToolbar",
170       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
171     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
172       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
173     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
174       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
175     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
176       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
177     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
178       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
179     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
180       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
181     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
182       SP_VERB_INVALID, 0, 0},
183     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
184       SP_VERB_INVALID, 0, 0},
185     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
186       SP_VERB_INVALID, 0, 0},
187     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
188       SP_VERB_INVALID, 0, 0},
189     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
190       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
191     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
192 };
195 static gchar const * ui_descr =
196         "<ui>"
197         "  <toolbar name='SelectToolbar'>"
198         "    <toolitem action='EditSelectAll' />"
199         "    <toolitem action='EditSelectAllInAllLayers' />"
200         "    <toolitem action='EditDeselect' />"
201         "    <separator />"
202         "    <toolitem action='ObjectRotate90CCW' />"
203         "    <toolitem action='ObjectRotate90' />"
204         "    <toolitem action='ObjectFlipHorizontally' />"
205         "    <toolitem action='ObjectFlipVertically' />"
206         "    <separator />"
207         "    <toolitem action='SelectionToBack' />"
208         "    <toolitem action='SelectionLower' />"
209         "    <toolitem action='SelectionRaise' />"
210         "    <toolitem action='SelectionToFront' />"
211         "    <separator />"
212         "    <toolitem action='XAction' />"
213         "    <toolitem action='YAction' />"
214         "    <toolitem action='WidthAction' />"
215         "    <toolitem action='LockAction' />"
216         "    <toolitem action='HeightAction' />"
217         "    <toolitem action='UnitsAction' />"
218         "    <separator />"
219         "    <toolitem action='transform_affect_label' />"
220         "    <toolitem action='transform_stroke' />"
221         "    <toolitem action='transform_corners' />"
222         "    <toolitem action='transform_gradient' />"
223         "    <toolitem action='transform_pattern' />"
224         "  </toolbar>"
226         "  <toolbar name='NodeToolbar'>"
227         "    <toolitem action='NodeInsertAction' />"
228         "    <toolitem action='NodeDeleteAction' />"
229         "    <separator />"
230         "    <toolitem action='NodeJoinAction' />"
231         "    <toolitem action='NodeJoinSegmentAction' />"
232         "    <toolitem action='NodeDeleteSegmentAction' />"
233         "    <toolitem action='NodeBreakAction' />"
234         "    <separator />"
235         "    <toolitem action='NodeCuspAction' />"
236         "    <toolitem action='NodeSmoothAction' />"
237         "    <toolitem action='NodeSymmetricAction' />"
238         "    <separator />"
239         "    <toolitem action='NodeLineAction' />"
240         "    <toolitem action='NodeCurveAction' />"
241         "    <separator />"
242         "    <toolitem action='ObjectToPath' />"
243         "    <toolitem action='StrokeToPath' />"
244         "    <separator />"
245         "    <toolitem action='NodesShowHandlesAction' />"
246         "    <separator />"
247         "    <toolitem action='EditNextLPEParameterAction' />"
248         "    <separator />"
249         "    <toolitem action='NodeXAction' />"
250         "    <toolitem action='NodeYAction' />"
251         "  </toolbar>"
253         "  <toolbar name='TweakToolbar'>"
254         "    <toolitem action='TweakWidthAction' />"
255         "    <separator />"
256         "    <toolitem action='TweakForceAction' />"
257         "    <toolitem action='TweakPressureAction' />"
258         "    <separator />"
259         "    <toolitem action='TweakModeAction' />"
260         "    <separator />"
261         "    <toolitem action='TweakFidelityAction' />"
262         "    <separator />"
263         "    <toolitem action='TweakChannelsLabel' />"
264         "    <toolitem action='TweakDoH' />"
265         "    <toolitem action='TweakDoS' />"
266         "    <toolitem action='TweakDoL' />"
267         "    <toolitem action='TweakDoO' />"
268         "  </toolbar>"
270         "  <toolbar name='ZoomToolbar'>"
271         "    <toolitem action='ZoomIn' />"
272         "    <toolitem action='ZoomOut' />"
273         "    <separator />"
274         "    <toolitem action='Zoom1:0' />"
275         "    <toolitem action='Zoom1:2' />"
276         "    <toolitem action='Zoom2:1' />"
277         "    <separator />"
278         "    <toolitem action='ZoomSelection' />"
279         "    <toolitem action='ZoomDrawing' />"
280         "    <toolitem action='ZoomPage' />"
281         "    <toolitem action='ZoomPageWidth' />"
282         "    <separator />"
283         "    <toolitem action='ZoomPrev' />"
284         "    <toolitem action='ZoomNext' />"
285         "  </toolbar>"
287         "  <toolbar name='StarToolbar'>"
288         "    <separator />"
289         "    <toolitem action='StarStateAction' />"
290         "    <separator />"
291         "    <toolitem action='FlatAction' />"
292         "    <separator />"
293         "    <toolitem action='MagnitudeAction' />"
294         "    <toolitem action='SpokeAction' />"
295         "    <toolitem action='RoundednessAction' />"
296         "    <toolitem action='RandomizationAction' />"
297         "    <separator />"
298         "    <toolitem action='StarResetAction' />"
299         "  </toolbar>"
301         "  <toolbar name='RectToolbar'>"
302         "    <toolitem action='RectStateAction' />"
303         "    <toolitem action='RectWidthAction' />"
304         "    <toolitem action='RectHeightAction' />"
305         "    <toolitem action='RadiusXAction' />"
306         "    <toolitem action='RadiusYAction' />"
307         "    <toolitem action='RectUnitsAction' />"
308         "    <separator />"
309         "    <toolitem action='RectResetAction' />"
310         "  </toolbar>"
312         "  <toolbar name='3DBoxToolbar'>"
313         "    <toolitem action='3DBoxPosAngleXAction' />"
314         "    <toolitem action='3DBoxVPXAction' />"
315         "    <separator />"
316         "    <toolitem action='3DBoxPosAngleYAction' />"
317         "    <toolitem action='3DBoxVPYAction' />"
318         "    <separator />"
319         "    <toolitem action='3DBoxPosAngleZAction' />"
320         "    <toolitem action='3DBoxVPZAction' />"
321         "    <separator />"
322         "  </toolbar>"
324         "  <toolbar name='SpiralToolbar'>"
325         "    <toolitem action='SpiralStateAction' />"
326         "    <toolitem action='SpiralRevolutionAction' />"
327         "    <toolitem action='SpiralExpansionAction' />"
328         "    <toolitem action='SpiralT0Action' />"
329         "    <separator />"
330         "    <toolitem action='SpiralResetAction' />"
331         "  </toolbar>"
333         "  <toolbar name='PenToolbar'>"
334         "  </toolbar>"
336         "  <toolbar name='PencilToolbar'>"
337         "  </toolbar>"
339         "  <toolbar name='CalligraphyToolbar'>"
340         "    <separator />"
341         "    <toolitem action='CalligraphyWidthAction' />"
342         "    <toolitem action='PressureAction' />"
343         "    <toolitem action='TraceAction' />"
344         "    <toolitem action='ThinningAction' />"
345         "    <separator />"
346         "    <toolitem action='AngleAction' />"
347         "    <toolitem action='TiltAction' />"
348         "    <toolitem action='FixationAction' />"
349         "    <separator />"
350         "    <toolitem action='CapRoundingAction' />"
351         "    <separator />"
352         "    <toolitem action='TremorAction' />"
353         "    <toolitem action='WiggleAction' />"
354         "    <toolitem action='MassAction' />"
355         "    <separator />"
356         "    <toolitem action='CalligraphyResetAction' />"
357         "  </toolbar>"
359         "  <toolbar name='ArcToolbar'>"
360         "    <toolitem action='ArcStateAction' />"
361         "    <separator />"
362         "    <toolitem action='ArcStartAction' />"
363         "    <toolitem action='ArcEndAction' />"
364         "    <separator />"
365         "    <toolitem action='ArcOpenAction' />"
366         "    <separator />"
367         "    <toolitem action='ArcResetAction' />"
368         "    <separator />"
369         "  </toolbar>"
371         "  <toolbar name='PaintbucketToolbar'>"
372         "    <toolitem action='ChannelsAction' />"
373         "    <separator />"
374         "    <toolitem action='ThresholdAction' />"
375         "    <separator />"
376         "    <toolitem action='OffsetAction' />"
377         "    <toolitem action='PaintbucketUnitsAction' />"
378         "    <separator />"
379         "    <toolitem action='AutoGapAction' />"
380         "    <separator />"
381         "    <toolitem action='PaintbucketResetAction' />"
382         "  </toolbar>"
384         "  <toolbar name='DropperToolbar'>"
385         "    <toolitem action='DropperPickAlphaAction' />"
386         "    <toolitem action='DropperSetAlphaAction' />"
387         "  </toolbar>"
389         "  <toolbar name='ConnectorToolbar'>"
390         "    <toolitem action='ConnectorAvoidAction' />"
391         "    <toolitem action='ConnectorIgnoreAction' />"
392         "    <toolitem action='ConnectorSpacingAction' />"
393         "    <toolitem action='ConnectorGraphAction' />"
394         "    <toolitem action='ConnectorLengthAction' />"
395         "    <toolitem action='ConnectorDirectedAction' />"
396         "    <toolitem action='ConnectorOverlapAction' />"
397         "  </toolbar>"
399         "</ui>"
402 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
404 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
406 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
407 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
409 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
410 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
412 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
413 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
415 /* Global text entry widgets necessary for update */
416 /* GtkWidget *dropper_rgb_entry,
417           *dropper_opacity_entry ; */
418 // should be made a private member once this is converted to class
420 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
421     connection->disconnect();
422     delete connection;
425 static void purge_repr_listener( GObject* obj, GObject* tbl )
427     (void)obj;
428     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
429     if (oldrepr) { // remove old listener
430         sp_repr_remove_listener_by_data(oldrepr, tbl);
431         Inkscape::GC::release(oldrepr);
432         oldrepr = 0;
433         g_object_set_data( tbl, "repr", NULL );
434     }
437 GtkWidget *
438 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
439                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
440                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
442     SPAction *action = verb->get_action(view);
443     if (!action) return NULL;
445     SPAction *doubleclick_action;
446     if (doubleclick_verb)
447         doubleclick_action = doubleclick_verb->get_action(view);
448     else
449         doubleclick_action = NULL;
451     /* fixme: Handle sensitive/unsensitive */
452     /* fixme: Implement sp_button_new_from_action */
453     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
454     gtk_widget_show(b);
455     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
457     return b;
460 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
461                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
463     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
466 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
467                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
469     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
473 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
475     SPAction* targetAction = SP_ACTION(user_data);
476     if ( targetAction ) {
477         sp_action_perform( targetAction, NULL );
478     }
481 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
483     if ( data ) {
484         GtkAction* act = GTK_ACTION(data);
485         gtk_action_set_sensitive( act, sensitive );
486     }
489 static SPActionEventVector action_event_vector = {
490     {NULL},
491     NULL,
492     NULL,
493     sp_action_action_set_sensitive,
494     NULL,
495     NULL
496 };
498 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
500     GtkAction* act = 0;
502     SPAction* targetAction = verb->get_action(view);
503     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
504     act = GTK_ACTION(inky);
505     gtk_action_set_sensitive( act, targetAction->sensitive );
507     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
509     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
510     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
512     return act;
515 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
517     Inkscape::UI::View::View *view = desktop;
518     gint verbsToUse[] = {
519         // disabled until we have icons for them:
520         //find
521         //SP_VERB_EDIT_TILE,
522         //SP_VERB_EDIT_UNTILE,
523         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
524         SP_VERB_DIALOG_DISPLAY,
525         SP_VERB_DIALOG_FILL_STROKE,
526         SP_VERB_DIALOG_NAMEDVIEW,
527         SP_VERB_DIALOG_TEXT,
528         SP_VERB_DIALOG_XML_EDITOR,
529         SP_VERB_EDIT_CLONE,
530         SP_VERB_EDIT_COPY,
531         SP_VERB_EDIT_CUT,
532         SP_VERB_EDIT_DUPLICATE,
533         SP_VERB_EDIT_PASTE,
534         SP_VERB_EDIT_REDO,
535         SP_VERB_EDIT_UNDO,
536         SP_VERB_EDIT_UNLINK_CLONE,
537         SP_VERB_FILE_EXPORT,
538         SP_VERB_FILE_IMPORT,
539         SP_VERB_FILE_NEW,
540         SP_VERB_FILE_OPEN,
541         SP_VERB_FILE_PRINT,
542         SP_VERB_FILE_SAVE,
543         SP_VERB_OBJECT_TO_CURVE,
544         SP_VERB_SELECTION_GROUP,
545         SP_VERB_SELECTION_OUTLINE,
546         SP_VERB_SELECTION_UNGROUP,
547         SP_VERB_ZOOM_1_1,
548         SP_VERB_ZOOM_1_2,
549         SP_VERB_ZOOM_2_1,
550         SP_VERB_ZOOM_DRAWING,
551         SP_VERB_ZOOM_IN,
552         SP_VERB_ZOOM_NEXT,
553         SP_VERB_ZOOM_OUT,
554         SP_VERB_ZOOM_PAGE,
555         SP_VERB_ZOOM_PAGE_WIDTH,
556         SP_VERB_ZOOM_PREV,
557         SP_VERB_ZOOM_SELECTION,
558     };
560     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
561     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
563     static std::map<SPDesktop*, GtkActionGroup*> groups;
564     GtkActionGroup* mainActions = 0;
565     if ( groups.find(desktop) != groups.end() ) {
566         mainActions = groups[desktop];
567     }
569     if ( !mainActions ) {
570         mainActions = gtk_action_group_new("main");
571         groups[desktop] = mainActions;
572     }
574     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
575         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
576         if ( verb ) {
577             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
578                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
579                 gtk_action_group_add_action( mainActions, act );
580             }
581         }
582     }
584     return mainActions;
588 GtkWidget *
589 sp_tool_toolbox_new()
591     GtkTooltips *tt = gtk_tooltips_new();
592     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
594     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
595     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
597     gtk_widget_set_sensitive(tb, FALSE);
599     GtkWidget *hb = gtk_handle_box_new();
600     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
601     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
602     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
604     gtk_container_add(GTK_CONTAINER(hb), tb);
605     gtk_widget_show(GTK_WIDGET(tb));
607     sigc::connection* conn = new sigc::connection;
608     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
610     return hb;
613 static void
614 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
616     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
617     gtk_widget_queue_resize(child);
620 static void
621 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
623     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
624     gtk_widget_queue_resize(child);
627 GtkWidget *
628 sp_aux_toolbox_new()
630     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
632     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
633     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
634     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
635     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
636     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
638     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
639     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
641     gtk_widget_set_sensitive(tb, FALSE);
643     GtkWidget *hb = gtk_handle_box_new();
644     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
645     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
646     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
648     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
649     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
651     gtk_container_add(GTK_CONTAINER(hb), tb);
652     gtk_widget_show(GTK_WIDGET(tb));
654     sigc::connection* conn = new sigc::connection;
655     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
657     return hb;
660 //####################################
661 //# Commands Bar
662 //####################################
664 GtkWidget *
665 sp_commands_toolbox_new()
667     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
669     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
670     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
671     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
672     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
673     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
675     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
676     gtk_widget_set_sensitive(tb, FALSE);
678     GtkWidget *hb = gtk_handle_box_new();
679     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
680     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
681     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
683     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
684     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
686     gtk_container_add(GTK_CONTAINER(hb), tb);
687     gtk_widget_show(GTK_WIDGET(tb));
689     sigc::connection* conn = new sigc::connection;
690     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
692     return hb;
695 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
696                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
697                                                        gchar const *path, gchar const *data, gdouble def,
698                                                        GtkWidget *focusTarget,
699                                                        GtkWidget *us,
700                                                        GObject *dataKludge,
701                                                        gboolean altx, gchar const *altx_mark,
702                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
703                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
704                                                        void (*callback)(GtkAdjustment *, GObject *),
705                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
707     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
708                                                              lower, upper, step, page, page ) );
709     if (us) {
710         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
711     }
713     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
715     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
716     if ( shortLabel ) {
717         g_object_set( act, "short_label", shortLabel, NULL );
718     }
720     if ( (descrCount > 0) && descrLabels && descrValues ) {
721         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
722     }
724     if ( focusTarget ) {
725         ege_adjustment_action_set_focuswidget( act, focusTarget );
726     }
728     if ( altx && altx_mark ) {
729         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
730     }
732     if ( dataKludge ) {
733         g_object_set_data( dataKludge, data, adj );
734     }
736     // Using a cast just to make sure we pass in the right kind of function pointer
737     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
739     return act;
743 //####################################
744 //# node editing callbacks
745 //####################################
747 /**
748  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
749  */
750 static ShapeEditor *get_current_shape_editor()
752     if (!SP_ACTIVE_DESKTOP) {
753         return NULL;
754     }
756     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
758     if (!SP_IS_NODE_CONTEXT(event_context)) {
759         return NULL;
760     }
762     return SP_NODE_CONTEXT(event_context)->shape_editor;
766 void
767 sp_node_path_edit_add(void)
769     ShapeEditor *shape_editor = get_current_shape_editor();
770     if (shape_editor) shape_editor->add_node();
773 void
774 sp_node_path_edit_delete(void)
776     ShapeEditor *shape_editor = get_current_shape_editor();
777     if (shape_editor) shape_editor->delete_nodes();
780 void
781 sp_node_path_edit_delete_segment(void)
783     ShapeEditor *shape_editor = get_current_shape_editor();
784     if (shape_editor) shape_editor->delete_segment();
787 void
788 sp_node_path_edit_break(void)
790     ShapeEditor *shape_editor = get_current_shape_editor();
791     if (shape_editor) shape_editor->break_at_nodes();
794 void
795 sp_node_path_edit_join(void)
797     ShapeEditor *shape_editor = get_current_shape_editor();
798     if (shape_editor) shape_editor->join_nodes();
801 void
802 sp_node_path_edit_join_segment(void)
804     ShapeEditor *shape_editor = get_current_shape_editor();
805     if (shape_editor) shape_editor->join_segments();
808 void
809 sp_node_path_edit_toline(void)
811     ShapeEditor *shape_editor = get_current_shape_editor();
812     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
815 void
816 sp_node_path_edit_tocurve(void)
818     ShapeEditor *shape_editor = get_current_shape_editor();
819     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
822 void
823 sp_node_path_edit_cusp(void)
825     ShapeEditor *shape_editor = get_current_shape_editor();
826     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
829 void
830 sp_node_path_edit_smooth(void)
832     ShapeEditor *shape_editor = get_current_shape_editor();
833     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
836 void
837 sp_node_path_edit_symmetrical(void)
839     ShapeEditor *shape_editor = get_current_shape_editor();
840     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
843 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
844     bool show = gtk_toggle_action_get_active( act );
845     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
846     ShapeEditor *shape_editor = get_current_shape_editor();
847     if (shape_editor) shape_editor->show_handles(show);
850 void
851 sp_node_path_edit_nextLPEparam (GtkAction *act, gpointer data) {
852     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
855 /* is called when the node selection is modified */
856 static void
857 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
859     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
860     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
861     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
862     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
864     // quit if run by the attr_changed listener
865     if (g_object_get_data( tbl, "freeze" )) {
866         return;
867     }
869     // in turn, prevent listener from responding
870     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
872     ShapeEditor *shape_editor = get_current_shape_editor();
873     if (shape_editor && shape_editor->has_nodepath()) {
874         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
875         int n_selected = 0;
876         if (nodepath) {
877             n_selected = nodepath->numSelected();
878         }
880         if (n_selected == 0) {
881             gtk_action_set_sensitive(xact, FALSE);
882             gtk_action_set_sensitive(yact, FALSE);
883         } else {
884             gtk_action_set_sensitive(xact, TRUE);
885             gtk_action_set_sensitive(yact, TRUE);
886             NR::Coord oldx = gtk_adjustment_get_value(xadj);
887             NR::Coord oldy = gtk_adjustment_get_value(xadj);
889             if (n_selected == 1) {
890                 NR::Point sel_node = nodepath->singleSelectedCoords();
891                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
892                     gtk_adjustment_set_value(xadj, sel_node[NR::X]);
893                     gtk_adjustment_set_value(yadj, sel_node[NR::Y]);
894                 }
895             } else {
896                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
897                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
898                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
899                     /* Note: Currently x and y will always have a value, even if the coordinates of the
900                        selected nodes don't coincide (in this case we use the coordinates of the center
901                        of the bounding box). So the entries are never set to zero. */
902                     gtk_adjustment_set_value(xadj, x ? (*x) : 0.0); // FIXME: Maybe we should clear the entry
903                     gtk_adjustment_set_value(yadj, y ? (*y) : 0.0); //        fields, not set them to zero.
904                 }
905             }
906         }
907     } else {
908         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
909         gtk_action_set_sensitive(xact, FALSE);
910         gtk_action_set_sensitive(yact, FALSE);
911     }
913     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
916 static void
917 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
919     // quit if run by the attr_changed listener
920     if (g_object_get_data( tbl, "freeze" )) {
921         return;
922     }
924     // in turn, prevent listener from responding
925     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
927     ShapeEditor *shape_editor = get_current_shape_editor();
928     if (shape_editor && shape_editor->has_nodepath()) {
929         if (!strcmp(value_name, "x")) {
930             sp_node_selected_move_absolute(shape_editor->get_nodepath(), gtk_adjustment_get_value(adj), NR::X);
931         }
932         if (!strcmp(value_name, "y")) {
933             sp_node_selected_move_absolute(shape_editor->get_nodepath(), gtk_adjustment_get_value(adj), NR::Y);
934         }
935     }
937     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
940 static void
941 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
943     sp_node_path_value_changed(adj, tbl, "x");
946 static void
947 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
949     sp_node_path_value_changed(adj, tbl, "y");
952 //################################
953 //##    Node Editing Toolbox    ##
954 //################################
956 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
958     {
959         InkAction* inky = ink_action_new( "NodeInsertAction",
960                                           _("Insert node"),
961                                           _("Insert new nodes into selected segments"),
962                                           "node_insert",
963                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
964         g_object_set( inky, "short_label", _("Insert"), NULL );
965         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
966         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
967     }
969     {
970         InkAction* inky = ink_action_new( "NodeDeleteAction",
971                                           _("Delete node"),
972                                           _("Delete selected nodes"),
973                                           "node_delete",
974                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
975         g_object_set( inky, "short_label", _("Delete"), NULL );
976         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
977         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
978     }
980     {
981         InkAction* inky = ink_action_new( "NodeJoinAction",
982                                           _("Join endnodes"),
983                                           _("Join selected endnodes"),
984                                           "node_join",
985                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
986         g_object_set( inky, "short_label", _("Join"), NULL );
987         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
988         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
989     }
991     {
992         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
993                                           _("Join Segment"),
994                                           _("Join selected endnodes with a new segment"),
995                                           "node_join_segment",
996                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
997         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
998         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
999     }
1001     {
1002         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1003                                           _("Delete Segment"),
1004                                           _("Split path between two non-endpoint nodes"),
1005                                           "node_delete_segment",
1006                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1007         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1008         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1009     }
1011     {
1012         InkAction* inky = ink_action_new( "NodeBreakAction",
1013                                           _("Node Break"),
1014                                           _("Break path at selected nodes"),
1015                                           "node_break",
1016                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1017         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1018         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1019     }
1021     {
1022         InkAction* inky = ink_action_new( "NodeCuspAction",
1023                                           _("Node Cusp"),
1024                                           _("Make selected nodes corner"),
1025                                           "node_cusp",
1026                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1027         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1028         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1029     }
1031     {
1032         InkAction* inky = ink_action_new( "NodeSmoothAction",
1033                                           _("Node Smooth"),
1034                                           _("Make selected nodes smooth"),
1035                                           "node_smooth",
1036                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1037         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1038         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1039     }
1041     {
1042         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1043                                           _("Node Symmetric"),
1044                                           _("Make selected nodes symmetric"),
1045                                           "node_symmetric",
1046                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1047         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1048         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1049     }
1051     {
1052         InkAction* inky = ink_action_new( "NodeLineAction",
1053                                           _("Node Line"),
1054                                           _("Make selected segments lines"),
1055                                           "node_line",
1056                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1057         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1058         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1059     }
1061     {
1062         InkAction* inky = ink_action_new( "NodeCurveAction",
1063                                           _("Node Curve"),
1064                                           _("Make selected segments curves"),
1065                                           "node_curve",
1066                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1067         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1068         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1069     }
1071     {
1072         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1073                                                       _("Show Handles"),
1074                                                       _("Show the Bezier handles of selected nodes"),
1075                                                       "nodes_show_handles",
1076                                                       Inkscape::ICON_SIZE_DECORATION );
1077         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1078         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1079         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1080     }
1082     {
1083         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1084                                           _("Next Path Effect Parameter"),
1085                                           _("Show next Path Effect parameter for editing"),
1086                                           "edit_next_parameter",
1087                                           Inkscape::ICON_SIZE_DECORATION );
1088         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1089         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1090     }
1092     /* X coord of selected node(s) */
1093     {
1094         EgeAdjustmentAction* eact = 0;
1095         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1096         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1097         eact = create_adjustment_action( "NodeXAction",
1098                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1099                                          "tools.nodes", "Xcoord", 0,
1100                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1101                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1102                                          labels, values, G_N_ELEMENTS(labels),
1103                                          sp_node_path_x_value_changed );
1104         g_object_set_data( holder, "nodes_x_action", eact );
1105         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1106         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1107     }
1109     /* Y coord of selected node(s) */
1110     {
1111         EgeAdjustmentAction* eact = 0;
1112         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1113         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1114         eact = create_adjustment_action( "NodeYAction",
1115                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1116                                          "tools.nodes", "Ycoord", 0,
1117                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1118                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1119                                          labels, values, G_N_ELEMENTS(labels),
1120                                          sp_node_path_y_value_changed );
1121         g_object_set_data( holder, "nodes_y_action", eact );
1122         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1123         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1124     }
1126     sigc::connection *connection = new sigc::connection (
1127         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1128         );
1130     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1131     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1132 } // end of sp_node_toolbox_prep()
1135 //########################
1136 //##    Zoom Toolbox    ##
1137 //########################
1139 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1141     // no custom GtkAction setup needed
1142 } // end of sp_zoom_toolbox_prep()
1144 void
1145 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1147     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")));
1151 void
1152 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1154     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")));
1157 void
1158 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1160     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")));
1163 static void
1164 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1166     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1167     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1169     if (old_desktop) {
1170         GList *children, *iter;
1172         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1173         for ( iter = children ; iter ; iter = iter->next ) {
1174             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1175         }
1176         g_list_free(children);
1177     }
1179     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1181     if (desktop) {
1182         gtk_widget_set_sensitive(toolbox, TRUE);
1183         setup_func(toolbox, desktop);
1184         update_func(desktop, desktop->event_context, toolbox);
1185         *conn = desktop->connectEventContextChanged
1186             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1187     } else {
1188         gtk_widget_set_sensitive(toolbox, FALSE);
1189     }
1191 } // end of toolbox_set_desktop()
1194 static void
1195 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1197     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1198     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1199     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1200         // "toolbox.tools" was not set. Fallback to older value
1201         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1203         // Copy the setting forwards
1204         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1205     }
1206     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1208     for (int i = 0 ; tools[i].type_name ; i++ ) {
1209         GtkWidget *button =
1210             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1211                                                               SP_BUTTON_TYPE_TOGGLE,
1212                                                               Inkscape::Verb::get(tools[i].verb),
1213                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1214                                                               desktop,
1215                                                               tooltips );
1217         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1218                            (gpointer)button );
1219     }
1223 static void
1224 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1226     gchar const *const tname = ( eventcontext
1227                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1228                                  : NULL );
1229     for (int i = 0 ; tools[i].type_name ; i++ ) {
1230         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1231         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1232     }
1235 static void
1236 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1238     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1239     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1240     GtkUIManager* mgr = gtk_ui_manager_new();
1241     GError* errVal = 0;
1242     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1243     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1245     std::map<std::string, GtkWidget*> dataHolders;
1247     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1248         if ( aux_toolboxes[i].prep_func ) {
1249             // converted to GtkActions and UIManager
1251             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1252             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1253             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1254             dataHolders[aux_toolboxes[i].type_name] = kludge;
1255             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1256         } else {
1258             GtkWidget *sub_toolbox = 0;
1259             if (aux_toolboxes[i].create_func == NULL)
1260                 sub_toolbox = sp_empty_toolbox_new(desktop);
1261             else {
1262                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1263             }
1265             gtk_size_group_add_widget( grouper, sub_toolbox );
1267             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1268             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1270         }
1271     }
1273     // Second pass to create toolbars *after* all GtkActions are created
1274     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1275         if ( aux_toolboxes[i].prep_func ) {
1276             // converted to GtkActions and UIManager
1278             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1280             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1281             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1283             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1284             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1285             g_free( tmp );
1286             tmp = 0;
1288             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1289             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1290             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1291                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1292             }
1293             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1296             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1298             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1299                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1300                 swatch->setDesktop( desktop );
1301                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1302                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1303                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1304                 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 );
1305             }
1307             gtk_widget_show_all( holder );
1308             sp_set_font_size_smaller( holder );
1310             gtk_size_group_add_widget( grouper, holder );
1312             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1313             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1314         }
1315     }
1317     g_object_unref( G_OBJECT(grouper) );
1320 static void
1321 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1323     gchar const *tname = ( eventcontext
1324                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1325                            : NULL );
1326     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1327         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1328         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1329             gtk_widget_show_all(sub_toolbox);
1330             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1331         } else {
1332             gtk_widget_hide(sub_toolbox);
1333         }
1334     }
1337 static void
1338 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1340     gchar const * descr =
1341         "<ui>"
1342         "  <toolbar name='CommandsToolbar'>"
1343         "    <toolitem action='FileNew' />"
1344         "    <toolitem action='FileOpen' />"
1345         "    <toolitem action='FileSave' />"
1346         "    <toolitem action='FilePrint' />"
1347         "    <separator />"
1348         "    <toolitem action='FileImport' />"
1349         "    <toolitem action='FileExport' />"
1350         "    <separator />"
1351         "    <toolitem action='EditUndo' />"
1352         "    <toolitem action='EditRedo' />"
1353         "    <separator />"
1354         "    <toolitem action='EditCopy' />"
1355         "    <toolitem action='EditCut' />"
1356         "    <toolitem action='EditPaste' />"
1357         "    <separator />"
1358         "    <toolitem action='ZoomSelection' />"
1359         "    <toolitem action='ZoomDrawing' />"
1360         "    <toolitem action='ZoomPage' />"
1361         "    <separator />"
1362         "    <toolitem action='EditDuplicate' />"
1363         "    <toolitem action='EditClone' />"
1364         "    <toolitem action='EditUnlinkClone' />"
1365         "    <separator />"
1366         "    <toolitem action='SelectionGroup' />"
1367         "    <toolitem action='SelectionUnGroup' />"
1368         "    <separator />"
1369         "    <toolitem action='DialogFillStroke' />"
1370         "    <toolitem action='DialogText' />"
1371         "    <toolitem action='DialogXMLEditor' />"
1372         "    <toolitem action='DialogAlignDistribute' />"
1373         "    <separator />"
1374         "    <toolitem action='DialogPreferences' />"
1375         "    <toolitem action='DialogDocumentProperties' />"
1376         "  </toolbar>"
1377         "</ui>";
1378     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1381     GtkUIManager* mgr = gtk_ui_manager_new();
1382     GError* errVal = 0;
1384     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1385     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1387     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1388     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1389         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1390     }
1391     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1392     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1393     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1396     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1399 static void
1400 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1404 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1406     gtk_widget_show(toolbox_toplevel);
1407     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1409     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1410     if (!shown_toolbox) {
1411         return;
1412     }
1413     gtk_widget_show(toolbox);
1415     // need to show the spacer, or the padding will be off
1416     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1417     gtk_widget_show(spacer);
1419     gtk_widget_show_all(shown_toolbox);
1422 void
1423 aux_toolbox_space(GtkWidget *tb, gint space)
1425     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1428 static GtkWidget *
1429 sp_empty_toolbox_new(SPDesktop *desktop)
1431     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1432     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1433     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1435     gtk_widget_show_all(tbl);
1436     sp_set_font_size_smaller (tbl);
1438     return tbl;
1441 // helper UI functions
1443 GtkWidget *
1444 sp_tb_spinbutton(
1445     gchar *label, gchar const *tooltip,
1446     gchar const *path, gchar const *data, gdouble def,
1447     GtkWidget *us,
1448     GtkWidget *tbl,
1449     gboolean altx, gchar const *altx_mark,
1450     gdouble lower, gdouble upper, gdouble step, gdouble page,
1451     void (*callback)(GtkAdjustment *, GtkWidget *),
1452     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1454     GtkTooltips *tt = gtk_tooltips_new();
1456     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1458     GtkWidget *l = gtk_label_new(label);
1459     gtk_widget_show(l);
1460     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1461     gtk_container_add(GTK_CONTAINER(hb), l);
1463     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1464                                       lower, upper, step, page, page);
1465     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1466     if (us)
1467         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1469     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1470     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1471     if (altx)
1472         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1473     gtk_widget_set_size_request(sb,
1474                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1475                                 AUX_SPINBUTTON_HEIGHT);
1476     gtk_widget_show(sb);
1477     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1478     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1479     gtk_container_add(GTK_CONTAINER(hb), sb);
1480     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1482     return hb;
1485 #define MODE_LABEL_WIDTH 70
1487 //########################
1488 //##       Star         ##
1489 //########################
1491 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1493     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1495     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1496         // do not remember prefs if this call is initiated by an undo change, because undoing object
1497         // creation sets bogus values to its attributes before it is deleted
1498         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1499     }
1501     // quit if run by the attr_changed listener
1502     if (g_object_get_data( dataKludge, "freeze" )) {
1503         return;
1504     }
1506     // in turn, prevent listener from responding
1507     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1509     bool modmade = false;
1511     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1512     GSList const *items = selection->itemList();
1513     for (; items != NULL; items = items->next) {
1514         if (SP_IS_STAR((SPItem *) items->data)) {
1515             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1516             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1517             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1518                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1519                                     + M_PI / (gint)adj->value));
1520             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1521             modmade = true;
1522         }
1523     }
1524     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1525                                    _("Star: Change number of corners"));
1527     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1530 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1532     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1534     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1535         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1536     }
1538     // quit if run by the attr_changed listener
1539     if (g_object_get_data( dataKludge, "freeze" )) {
1540         return;
1541     }
1543     // in turn, prevent listener from responding
1544     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1546     bool modmade = false;
1547     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1548     GSList const *items = selection->itemList();
1549     for (; items != NULL; items = items->next) {
1550         if (SP_IS_STAR((SPItem *) items->data)) {
1551             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1553             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1554             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1555             if (r2 < r1) {
1556                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1557             } else {
1558                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1559             }
1561             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1562             modmade = true;
1563         }
1564     }
1566     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1567                                    _("Star: Change spoke ratio"));
1569     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1572 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1574     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1575     bool flat = ege_select_one_action_get_active( act ) == 0;
1577     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1578         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1579                                     flat ? "true" : "false" );
1580     }
1582     // quit if run by the attr_changed listener
1583     if (g_object_get_data( dataKludge, "freeze" )) {
1584         return;
1585     }
1587     // in turn, prevent listener from responding
1588     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1590     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1591     GSList const *items = selection->itemList();
1592     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1593     bool modmade = false;
1595     if ( prop_action ) {
1596         gtk_action_set_sensitive( prop_action, !flat );
1597     }
1599     for (; items != NULL; items = items->next) {
1600         if (SP_IS_STAR((SPItem *) items->data)) {
1601             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1602             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1603             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1604             modmade = true;
1605         }
1606     }
1608     if (modmade) {
1609         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1610                          flat ? _("Make polygon") : _("Make star"));
1611     }
1613     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1616 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1618     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1620     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1621         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1622     }
1624     // quit if run by the attr_changed listener
1625     if (g_object_get_data( dataKludge, "freeze" )) {
1626         return;
1627     }
1629     // in turn, prevent listener from responding
1630     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1632     bool modmade = false;
1634     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1635     GSList const *items = selection->itemList();
1636     for (; items != NULL; items = items->next) {
1637         if (SP_IS_STAR((SPItem *) items->data)) {
1638             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1639             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1640             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1641             modmade = true;
1642         }
1643     }
1644     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1645                                    _("Star: Change rounding"));
1647     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1650 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1652     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1654     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1655         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1656     }
1658     // quit if run by the attr_changed listener
1659     if (g_object_get_data( dataKludge, "freeze" )) {
1660         return;
1661     }
1663     // in turn, prevent listener from responding
1664     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1666     bool modmade = false;
1668     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1669     GSList const *items = selection->itemList();
1670     for (; items != NULL; items = items->next) {
1671         if (SP_IS_STAR((SPItem *) items->data)) {
1672             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1673             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1674             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1675             modmade = true;
1676         }
1677     }
1678     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1679                                    _("Star: Change randomization"));
1681     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1685 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1686                                        gchar const */*old_value*/, gchar const */*new_value*/,
1687                                        bool /*is_interactive*/, gpointer data)
1689     GtkWidget *tbl = GTK_WIDGET(data);
1691     // quit if run by the _changed callbacks
1692     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1693         return;
1694     }
1696     // in turn, prevent callbacks from responding
1697     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1699     GtkAdjustment *adj = 0;
1701     if (!strcmp(name, "inkscape:randomized")) {
1702         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1703         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1704     } else if (!strcmp(name, "inkscape:rounded")) {
1705         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1706         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1707     } else if (!strcmp(name, "inkscape:flatsided")) {
1708         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1709         char const *flatsides = repr->attribute("inkscape:flatsided");
1710         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1711         if ( flatsides && !strcmp(flatsides,"false") ) {
1712             ege_select_one_action_set_active( flat_action, 1 );
1713             gtk_action_set_sensitive( prop_action, TRUE );
1714         } else {
1715             ege_select_one_action_set_active( flat_action, 0 );
1716             gtk_action_set_sensitive( prop_action, FALSE );
1717         }
1718     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1719         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1720         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1721         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1722         if (r2 < r1) {
1723             gtk_adjustment_set_value(adj, r2/r1);
1724         } else {
1725             gtk_adjustment_set_value(adj, r1/r2);
1726         }
1727     } else if (!strcmp(name, "sodipodi:sides")) {
1728         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1729         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1730     }
1732     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1736 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1738     NULL, /* child_added */
1739     NULL, /* child_removed */
1740     star_tb_event_attr_changed,
1741     NULL, /* content_changed */
1742     NULL  /* order_changed */
1743 };
1746 /**
1747  *  \param selection Should not be NULL.
1748  */
1749 static void
1750 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1752     int n_selected = 0;
1753     Inkscape::XML::Node *repr = NULL;
1755     purge_repr_listener( tbl, tbl );
1757     for (GSList const *items = selection->itemList();
1758          items != NULL;
1759          items = items->next)
1760     {
1761         if (SP_IS_STAR((SPItem *) items->data)) {
1762             n_selected++;
1763             repr = SP_OBJECT_REPR((SPItem *) items->data);
1764         }
1765     }
1767     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1769     if (n_selected == 0) {
1770         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1771     } else if (n_selected == 1) {
1772         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1774         if (repr) {
1775             g_object_set_data( tbl, "repr", repr );
1776             Inkscape::GC::anchor(repr);
1777             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1778             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1779         }
1780     } else {
1781         // FIXME: implement averaging of all parameters for multiple selected stars
1782         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1783         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1784     }
1788 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1790     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1791     // callbacks to lump all the changes for all selected objects in one undo step
1793     GtkAdjustment *adj = 0;
1795     // fixme: make settable in prefs!
1796     gint mag = 5;
1797     gdouble prop = 0.5;
1798     gboolean flat = FALSE;
1799     gdouble randomized = 0;
1800     gdouble rounded = 0;
1802     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1803     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1805     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1806     gtk_action_set_sensitive( sb2, !flat );
1808     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1809     gtk_adjustment_set_value(adj, mag);
1810     gtk_adjustment_value_changed(adj);
1812     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1813     gtk_adjustment_set_value(adj, prop);
1814     gtk_adjustment_value_changed(adj);
1816     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1817     gtk_adjustment_set_value(adj, rounded);
1818     gtk_adjustment_value_changed(adj);
1820     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1821     gtk_adjustment_set_value(adj, randomized);
1822     gtk_adjustment_value_changed(adj);
1826 void
1827 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1829     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1830     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1831     GtkWidget *l = gtk_label_new(NULL);
1832     gtk_label_set_markup(GTK_LABEL(l), title);
1833     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1834     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1835     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1839 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1841     {
1842         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1843         ege_output_action_set_use_markup( act, TRUE );
1844         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1845         g_object_set_data( holder, "mode_action", act );
1846     }
1848     {
1849         //EgeAdjustmentAction* calligraphy_angle = 0;
1850         EgeAdjustmentAction* eact = 0;
1851         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1852         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1854         /* Flatsided checkbox */
1855         {
1856             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1858             GtkTreeIter iter;
1859             gtk_list_store_append( model, &iter );
1860             gtk_list_store_set( model, &iter,
1861                                 0, _("Polygon"),
1862                                 1, _("Regular polygon (with one handle) instead of a star"),
1863                                 2, "star_flat",
1864                                 -1 );
1866             gtk_list_store_append( model, &iter );
1867             gtk_list_store_set( model, &iter,
1868                                 0, _("Star"),
1869                                 1, _("Star instead of a regular polygon (with one handle)"),
1870                                 2, "star_angled",
1871                                 -1 );
1873             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1874             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1875             g_object_set_data( holder, "flat_action", act );
1877             ege_select_one_action_set_appearance( act, "full" );
1878             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1879             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1880             ege_select_one_action_set_icon_column( act, 2 );
1881             ege_select_one_action_set_tooltip_column( act, 1  );
1883             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1884             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1885         }
1887         /* Magnitude */
1888         {
1889         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1890         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1891         eact = create_adjustment_action( "MagnitudeAction",
1892                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1893                                          "tools.shapes.star", "magnitude", 3,
1894                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1895                                          3, 1024, 1, 5,
1896                                          labels, values, G_N_ELEMENTS(labels),
1897                                          sp_stb_magnitude_value_changed,
1898                                          1.0, 0 );
1899         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1900         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1901         }
1903         /* Spoke ratio */
1904         {
1905         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1906         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1907         eact = create_adjustment_action( "SpokeAction",
1908                                          _("Spoke ratio"), _("Spoke ratio:"),
1909                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1910                                          // Base radius is the same for the closest handle.
1911                                          _("Base radius to tip radius ratio"),
1912                                          "tools.shapes.star", "proportion", 0.5,
1913                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1914                                          0.01, 1.0, 0.01, 0.1,
1915                                          labels, values, G_N_ELEMENTS(labels),
1916                                          sp_stb_proportion_value_changed );
1917         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1918         g_object_set_data( holder, "prop_action", eact );
1919         }
1921         if ( !isFlatSided ) {
1922             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1923         } else {
1924             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1925         }
1927         /* Roundedness */
1928         {
1929         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1930         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1931         eact = create_adjustment_action( "RoundednessAction",
1932                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1933                                          "tools.shapes.star", "rounded", 0.0,
1934                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1935                                          -10.0, 10.0, 0.01, 0.1,
1936                                          labels, values, G_N_ELEMENTS(labels),
1937                                          sp_stb_rounded_value_changed );
1938         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1939         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1940         }
1942         /* Randomization */
1943         {
1944         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1945         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1946         eact = create_adjustment_action( "RandomizationAction",
1947                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1948                                          "tools.shapes.star", "randomized", 0.0,
1949                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1950                                          -10.0, 10.0, 0.001, 0.01,
1951                                          labels, values, G_N_ELEMENTS(labels),
1952                                          sp_stb_randomized_value_changed, 0.1, 3 );
1953         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1954         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1955         }
1956     }
1958     {
1959         /* Reset */
1960         {
1961             GtkAction* act = gtk_action_new( "StarResetAction",
1962                                              _("Defaults"),
1963                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1964                                              GTK_STOCK_CLEAR );
1965             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1966             gtk_action_group_add_action( mainActions, act );
1967             gtk_action_set_sensitive( act, TRUE );
1968         }
1969     }
1971     sigc::connection *connection = new sigc::connection(
1972         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1973         );
1974     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1975     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1979 //########################
1980 //##       Rect         ##
1981 //########################
1983 static void sp_rtb_sensitivize( GObject *tbl )
1985     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1986     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1987     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1989     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1990         gtk_action_set_sensitive( not_rounded, FALSE );
1991     } else {
1992         gtk_action_set_sensitive( not_rounded, TRUE );
1993     }
1997 static void
1998 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1999                           void (*setter)(SPRect *, gdouble))
2001     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2003     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2004     SPUnit const *unit = tracker->getActiveUnit();
2006     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2007         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2008     }
2010     // quit if run by the attr_changed listener
2011     if (g_object_get_data( tbl, "freeze" )) {
2012         return;
2013     }
2015     // in turn, prevent listener from responding
2016     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2018     bool modmade = false;
2019     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2020     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2021         if (SP_IS_RECT(items->data)) {
2022             if (adj->value != 0) {
2023                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2024             } else {
2025                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2026             }
2027             modmade = true;
2028         }
2029     }
2031     sp_rtb_sensitivize( tbl );
2033     if (modmade) {
2034         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2035                                    _("Change rectangle"));
2036     }
2038     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2041 static void
2042 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2044     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2047 static void
2048 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2050     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2053 static void
2054 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2056     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2059 static void
2060 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2062     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2067 static void
2068 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2070     GtkAdjustment *adj = 0;
2072     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2073     gtk_adjustment_set_value(adj, 0.0);
2074     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2075     gtk_adjustment_value_changed(adj);
2077     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2078     gtk_adjustment_set_value(adj, 0.0);
2079     gtk_adjustment_value_changed(adj);
2081     sp_rtb_sensitivize( obj );
2084 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2085                                        gchar const */*old_value*/, gchar const */*new_value*/,
2086                                        bool /*is_interactive*/, gpointer data)
2088     GObject *tbl = G_OBJECT(data);
2090     // quit if run by the _changed callbacks
2091     if (g_object_get_data( tbl, "freeze" )) {
2092         return;
2093     }
2095     // in turn, prevent callbacks from responding
2096     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2098     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2099     SPUnit const *unit = tracker->getActiveUnit();
2101     gpointer item = g_object_get_data( tbl, "item" );
2102     if (item && SP_IS_RECT(item)) {
2103         {
2104             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2105             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2106             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2107         }
2109         {
2110             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2111             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2112             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2113         }
2115         {
2116             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2117             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2118             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2119         }
2121         {
2122             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2123             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2124             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2125         }
2126     }
2128     sp_rtb_sensitivize( tbl );
2130     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2134 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2135     NULL, /* child_added */
2136     NULL, /* child_removed */
2137     rect_tb_event_attr_changed,
2138     NULL, /* content_changed */
2139     NULL  /* order_changed */
2140 };
2142 /**
2143  *  \param selection should not be NULL.
2144  */
2145 static void
2146 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2148     int n_selected = 0;
2149     Inkscape::XML::Node *repr = NULL;
2150     SPItem *item = NULL;
2152     if ( g_object_get_data( tbl, "repr" ) ) {
2153         g_object_set_data( tbl, "item", NULL );
2154     }
2155     purge_repr_listener( tbl, tbl );
2157     for (GSList const *items = selection->itemList();
2158          items != NULL;
2159          items = items->next) {
2160         if (SP_IS_RECT((SPItem *) items->data)) {
2161             n_selected++;
2162             item = (SPItem *) items->data;
2163             repr = SP_OBJECT_REPR(item);
2164         }
2165     }
2167     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2169     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2171     if (n_selected == 0) {
2172         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2174         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2175         gtk_action_set_sensitive(w, FALSE);
2176         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2177         gtk_action_set_sensitive(h, FALSE);
2179     } else if (n_selected == 1) {
2180         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2181         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2183         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2184         gtk_action_set_sensitive(w, TRUE);
2185         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2186         gtk_action_set_sensitive(h, TRUE);
2188         if (repr) {
2189             g_object_set_data( tbl, "repr", repr );
2190             g_object_set_data( tbl, "item", item );
2191             Inkscape::GC::anchor(repr);
2192             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2193             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2194         }
2195     } else {
2196         // FIXME: implement averaging of all parameters for multiple selected
2197         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2198         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2199         sp_rtb_sensitivize( tbl );
2200     }
2204 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2206     EgeAdjustmentAction* eact = 0;
2208     {
2209         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2210         ege_output_action_set_use_markup( act, TRUE );
2211         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2212         g_object_set_data( holder, "mode_action", act );
2213     }
2215     // rx/ry units menu: create
2216     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2217     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2218     // fixme: add % meaning per cent of the width/height
2219     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2220     g_object_set_data( holder, "tracker", tracker );
2222     /* W */
2223     {
2224         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2225         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2226         eact = create_adjustment_action( "RectWidthAction",
2227                                          _("Width"), _("W:"), _("Width of rectangle"),
2228                                          "tools.shapes.rect", "width", 0,
2229                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2230                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2231                                          labels, values, G_N_ELEMENTS(labels),
2232                                          sp_rtb_width_value_changed );
2233         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2234         g_object_set_data( holder, "width_action", eact );
2235         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2236         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2237     }
2239     /* H */
2240     {
2241         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2242         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2243         eact = create_adjustment_action( "RectHeightAction",
2244                                          _("Height"), _("H:"), _("Height of rectangle"),
2245                                          "tools.shapes.rect", "height", 0,
2246                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2247                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2248                                          labels, values, G_N_ELEMENTS(labels),
2249                                          sp_rtb_height_value_changed );
2250         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2251         g_object_set_data( holder, "height_action", eact );
2252         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2253         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2254     }
2256     /* rx */
2257     {
2258         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2259         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2260         eact = create_adjustment_action( "RadiusXAction",
2261                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2262                                          "tools.shapes.rect", "rx", 0,
2263                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2264                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2265                                          labels, values, G_N_ELEMENTS(labels),
2266                                          sp_rtb_rx_value_changed);
2267         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2268         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2269     }
2271     /* ry */
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( "RadiusYAction",
2276                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2277                                          "tools.shapes.rect", "ry", 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_ry_value_changed);
2282         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2283         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2284     }
2286     // add the units menu
2287     {
2288         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2289         gtk_action_group_add_action( mainActions, act );
2290     }
2292     /* Reset */
2293     {
2294         InkAction* inky = ink_action_new( "RectResetAction",
2295                                           _("Not rounded"),
2296                                           _("Make corners sharp"),
2297                                           "squared_corner",
2298                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2299         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2300         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2301         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2302         g_object_set_data( holder, "not_rounded", inky );
2303     }
2305     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2306     sp_rtb_sensitivize( holder );
2308     sigc::connection *connection = new sigc::connection(
2309         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2310         );
2311     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2312     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2315 //########################
2316 //##       3D Box       ##
2317 //########################
2319 static void sp_3dbox_toggle_vp_changed (GtkToggleAction */*act*/, GObject *dataKludge, Box3D::Axis axis)
2321     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2322     SPDocument *document = sp_desktop_document (desktop);
2323     Box3D::Perspective3D *persp = document->current_perspective;
2325     g_return_if_fail (is_single_axis_direction (axis));
2326     g_return_if_fail (persp);
2328     persp->toggle_boxes (axis);
2330     gchar *str;
2331     switch (axis) {
2332         case Box3D::X:
2333             str = g_strdup ("box3d_angle_x_action");
2334             break;
2335         case Box3D::Y:
2336             str = g_strdup ("box3d_angle_y_action");
2337             break;
2338         case Box3D::Z:
2339             str = g_strdup ("box3d_angle_z_action");
2340             break;
2341         default:
2342             return;
2343     }
2344     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2345     if (angle_action) {
2346         gtk_action_set_sensitive (angle_action, !persp->get_vanishing_point (axis)->is_finite() );
2347     }
2349     // FIXME: Given how it is realized in the other tools, this is probably not the right way to do it,
2350     //        but without the if construct, we get continuous segfaults. Needs further investigation.
2351     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2352         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
2353                          _("3D Box: Change perspective"));
2354     }
2357 static void sp_3dbox_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2359     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::X);
2362 static void sp_3dbox_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2364     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Y);
2367 static void sp_3dbox_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2369     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Z);
2372 static void sp_3dbox_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Box3D::Axis axis )
2374     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2375     Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2377     if (persp) {
2378         double angle = adj->value * M_PI/180;
2379         persp->set_infinite_direction (axis, NR::Point (cos (angle), sin (angle)));
2381         // FIXME: See comment above; without the if construct we get segfaults during undo.
2382         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2383             sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2384                              _("3D Box: Change perspective"));
2385         }
2386     }
2387     //g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
2390 static void sp_3dbox_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2392     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::X);
2395 static void sp_3dbox_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2397     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Y);
2400 static void sp_3dbox_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2402     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Z);
2405 // normalize angle so that it lies in the interval [0,360]
2406 static double sp_3dbox_normalize_angle (double a) {
2407     double angle = a + ((int) (a/360.0))*360;
2408     if (angle < 0) {
2409         angle += 360.0;
2410     }
2411     return angle;
2414 static void sp_3dbox_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *name,
2415                                            gchar const */*old_value*/, gchar const */*new_value*/,
2416                                            bool /*is_interactive*/, gpointer data)
2418     GtkWidget *tbl = GTK_WIDGET(data);
2420     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2421     /***
2422     // quit if run by the _changed callbacks
2423     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2424         return;
2425     }
2427     // in turn, prevent callbacks from responding
2428     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2429     ***/
2431     if (!strcmp(name, "inkscape:perspective")) {
2432         GtkAdjustment *adj = 0;
2433         double angle;
2434         SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2435         Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2437         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2438         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::X)->get_angle());
2439         gtk_adjustment_set_value(adj, angle);
2441         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2442         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Y)->get_angle());
2443         gtk_adjustment_set_value(adj, angle);
2445         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2446         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Z)->get_angle());
2447         gtk_adjustment_set_value(adj, angle);
2448     }
2451 static Inkscape::XML::NodeEventVector sp_3dbox_tb_repr_events =
2453     NULL, /* child_added */
2454     NULL, /* child_removed */
2455     sp_3dbox_tb_event_attr_changed,
2456     NULL, /* content_changed */
2457     NULL  /* order_changed */
2458 };
2460 /**
2461  *  \param selection Should not be NULL.
2462  */
2463 static void
2464 sp_3dbox_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2466     Inkscape::XML::Node *repr = NULL;
2467     purge_repr_listener(tbl, tbl);
2469     SPItem *item = selection->singleItem();
2470     if (item) {
2471         repr = SP_OBJECT_REPR(item);
2472         if (repr) {
2473             g_object_set_data(tbl, "repr", repr);
2474             Inkscape::GC::anchor(repr);
2475             sp_repr_add_listener(repr, &sp_3dbox_tb_repr_events, tbl);
2476             sp_repr_synthesize_events(repr, &sp_3dbox_tb_repr_events, tbl);
2477         }
2478     }
2481 static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2483     EgeAdjustmentAction* eact = 0;
2484     SPDocument *document = sp_desktop_document (desktop);
2485     Box3D::Perspective3D *persp = document->current_perspective;
2486     bool toggled = false;
2488     /* angle of VP in X direction */
2489     eact = create_adjustment_action("3DBoxPosAngleXAction",
2490                                     _("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2491                                     "tools.shapes.3dbox", "dir_vp_x", persp->get_vanishing_point (Box3D::X)->get_angle(),
2492                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2493                                     0.0, 360.0, 1.0, 10.0,
2494                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2495                                     sp_3dbox_vpx_angle_changed,
2496                                     0.1, 1);
2497     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2498     g_object_set_data(holder, "box3d_angle_x_action", eact);
2499     if (!persp->get_vanishing_point (Box3D::X)->is_finite()) {
2500         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2501     } else {
2502         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2503     }
2505     /* toggle VP in X direction */
2506     {
2507     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2508                                                   _("Toggle VP in X direction"),
2509                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2510                                                   "toggle_vp_x",
2511                                                   Inkscape::ICON_SIZE_DECORATION);
2512     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2513     if (persp) {
2514         toggled = !persp->get_vanishing_point(Box3D::X)->is_finite();
2515     }
2516     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2517     /* we connect the signal after setting the state to avoid switching the state again */
2518     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_x_changed), holder);
2519     }
2521     /* angle of VP in Y direction */
2522     eact = create_adjustment_action("3DBoxPosAngleYAction",
2523                                     _("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2524                                     "tools.shapes.3dbox", "dir_vp_y", persp->get_vanishing_point (Box3D::Y)->get_angle(),
2525                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2526                                     0.0, 360.0, 1.0, 10.0,
2527                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2528                                     sp_3dbox_vpy_angle_changed,
2529                                     0.1, 1);
2530     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2531     g_object_set_data(holder, "box3d_angle_y_action", eact);
2532     if (!persp->get_vanishing_point (Box3D::Y)->is_finite()) {
2533         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2534     } else {
2535         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2536     }
2538     /* toggle VP in Y direction */
2539     {
2540     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2541                                                  _("Toggle VP in Y direction"),
2542                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2543                                                  "toggle_vp_y",
2544                                                  Inkscape::ICON_SIZE_DECORATION);
2545     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2546     if (persp) {
2547         toggled = !persp->get_vanishing_point(Box3D::Y)->is_finite();
2548     }
2549     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2550     /* we connect the signal after setting the state to avoid switching the state again */
2551     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_y_changed), holder);
2552     }
2554     /* angle of VP in Z direction */
2555     eact = create_adjustment_action("3DBoxPosAngleZAction",
2556                                     _("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2557                                     "tools.shapes.3dbox", "dir_vp_z", persp->get_vanishing_point (Box3D::Z)->get_angle(),
2558                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2559                                      0.0, 360.0, 1.0, 10.0,
2560                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2561                                     sp_3dbox_vpz_angle_changed,
2562                                     0.1, 1);
2564     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2565     g_object_set_data(holder, "box3d_angle_z_action", eact);
2566     if (!persp->get_vanishing_point (Box3D::Z)->is_finite()) {
2567         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2568     } else {
2569         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2570     }
2572     /* toggle VP in Z direction */
2573     {
2574     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2575                                                  _("Toggle VP in Z direction"),
2576                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2577                                                  "toggle_vp_z",
2578                                                  Inkscape::ICON_SIZE_DECORATION);
2579     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2580     if (persp) {
2581         toggled = !persp->get_vanishing_point(Box3D::Z)->is_finite();
2582     }
2583     /* we connect the signal after setting the state to avoid switching the state again */
2584     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2585     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_z_changed), holder);
2586     }
2588     sigc::connection *connection = new sigc::connection(
2589         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_3dbox_toolbox_selection_changed), (GObject *)holder))
2590        );
2591     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2592     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2595 //########################
2596 //##       Spiral       ##
2597 //########################
2599 static void
2600 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2602     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2604     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2605         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2606     }
2608     // quit if run by the attr_changed listener
2609     if (g_object_get_data( tbl, "freeze" )) {
2610         return;
2611     }
2613     // in turn, prevent listener from responding
2614     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2616     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2618     bool modmade = false;
2619     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2620          items != NULL;
2621          items = items->next)
2622     {
2623         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2624             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2625             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2626             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2627             modmade = true;
2628         }
2629     }
2631     g_free(namespaced_name);
2633     if (modmade) {
2634         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2635                                    _("Change spiral"));
2636     }
2638     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2641 static void
2642 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2644     sp_spl_tb_value_changed(adj, tbl, "revolution");
2647 static void
2648 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2650     sp_spl_tb_value_changed(adj, tbl, "expansion");
2653 static void
2654 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2656     sp_spl_tb_value_changed(adj, tbl, "t0");
2659 static void
2660 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2662     GtkWidget *tbl = GTK_WIDGET(obj);
2664     GtkAdjustment *adj;
2666     // fixme: make settable
2667     gdouble rev = 5;
2668     gdouble exp = 1.0;
2669     gdouble t0 = 0.0;
2671     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2672     gtk_adjustment_set_value(adj, rev);
2673     gtk_adjustment_value_changed(adj);
2675     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2676     gtk_adjustment_set_value(adj, exp);
2677     gtk_adjustment_value_changed(adj);
2679     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2680     gtk_adjustment_set_value(adj, t0);
2681     gtk_adjustment_value_changed(adj);
2683     spinbutton_defocus(GTK_OBJECT(tbl));
2687 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2688                                          gchar const */*old_value*/, gchar const */*new_value*/,
2689                                          bool /*is_interactive*/, gpointer data)
2691     GtkWidget *tbl = GTK_WIDGET(data);
2693     // quit if run by the _changed callbacks
2694     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2695         return;
2696     }
2698     // in turn, prevent callbacks from responding
2699     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2701     GtkAdjustment *adj;
2702     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2703     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2705     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2706     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2708     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2709     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2711     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2715 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2716     NULL, /* child_added */
2717     NULL, /* child_removed */
2718     spiral_tb_event_attr_changed,
2719     NULL, /* content_changed */
2720     NULL  /* order_changed */
2721 };
2723 static void
2724 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2726     int n_selected = 0;
2727     Inkscape::XML::Node *repr = NULL;
2729     purge_repr_listener( tbl, tbl );
2731     for (GSList const *items = selection->itemList();
2732          items != NULL;
2733          items = items->next)
2734     {
2735         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2736             n_selected++;
2737             repr = SP_OBJECT_REPR((SPItem *) items->data);
2738         }
2739     }
2741     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2743     if (n_selected == 0) {
2744         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2745     } else if (n_selected == 1) {
2746         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2748         if (repr) {
2749             g_object_set_data( tbl, "repr", repr );
2750             Inkscape::GC::anchor(repr);
2751             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2752             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2753         }
2754     } else {
2755         // FIXME: implement averaging of all parameters for multiple selected
2756         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2757         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2758     }
2762 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2764     EgeAdjustmentAction* eact = 0;
2766     {
2767         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2768         ege_output_action_set_use_markup( act, TRUE );
2769         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2770         g_object_set_data( holder, "mode_action", act );
2771     }
2773     /* Revolution */
2774     {
2775         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2776         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2777         eact = create_adjustment_action( "SpiralRevolutionAction",
2778                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2779                                          "tools.shapes.spiral", "revolution", 3.0,
2780                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2781                                          0.01, 1024.0, 0.1, 1.0,
2782                                          labels, values, G_N_ELEMENTS(labels),
2783                                          sp_spl_tb_revolution_value_changed, 1, 2);
2784         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2785     }
2787     /* Expansion */
2788     {
2789         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2790         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2791         eact = create_adjustment_action( "SpiralExpansionAction",
2792                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2793                                          "tools.shapes.spiral", "expansion", 1.0,
2794                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2795                                          0.0, 1000.0, 0.01, 1.0,
2796                                          labels, values, G_N_ELEMENTS(labels),
2797                                          sp_spl_tb_expansion_value_changed);
2798         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2799     }
2801     /* T0 */
2802     {
2803         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2804         gdouble values[] = {0, 0.5, 0.9};
2805         eact = create_adjustment_action( "SpiralT0Action",
2806                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2807                                          "tools.shapes.spiral", "t0", 0.0,
2808                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2809                                          0.0, 0.999, 0.01, 1.0,
2810                                          labels, values, G_N_ELEMENTS(labels),
2811                                          sp_spl_tb_t0_value_changed);
2812         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2813     }
2815     /* Reset */
2816     {
2817         InkAction* inky = ink_action_new( "SpiralResetAction",
2818                                           _("Defaults"),
2819                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2820                                           GTK_STOCK_CLEAR,
2821                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2822         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2823         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2824     }
2827     sigc::connection *connection = new sigc::connection(
2828         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2829         );
2830     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2831     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2834 //########################
2835 //##     Pen/Pencil    ##
2836 //########################
2839 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2841     // Put stuff here
2844 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2846     // Put stuff here
2849 //########################
2850 //##       Tweak        ##
2851 //########################
2853 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2855     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2858 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2860     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2863 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2865     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2868 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2870     int mode = ege_select_one_action_get_active( act );
2871     prefs_set_int_attribute("tools.tweak", "mode", mode);
2873     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2874     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2875     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2876     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2877     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2878     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2879     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2880         if (doh) gtk_action_set_sensitive (doh, TRUE);
2881         if (dos) gtk_action_set_sensitive (dos, TRUE);
2882         if (dol) gtk_action_set_sensitive (dol, TRUE);
2883         if (doo) gtk_action_set_sensitive (doo, TRUE);
2884         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2885         if (fid) gtk_action_set_sensitive (fid, FALSE);
2886     } else {
2887         if (doh) gtk_action_set_sensitive (doh, FALSE);
2888         if (dos) gtk_action_set_sensitive (dos, FALSE);
2889         if (dol) gtk_action_set_sensitive (dol, FALSE);
2890         if (doo) gtk_action_set_sensitive (doo, FALSE);
2891         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2892         if (fid) gtk_action_set_sensitive (fid, TRUE);
2893     }
2896 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2898     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2901 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2902     bool show = gtk_toggle_action_get_active( act );
2903     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2905 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2906     bool show = gtk_toggle_action_get_active( act );
2907     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2909 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
2910     bool show = gtk_toggle_action_get_active( act );
2911     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2913 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
2914     bool show = gtk_toggle_action_get_active( act );
2915     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
2918 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2920     {
2921         /* Width */
2922         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2923         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2924         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2925                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2926                                                               "tools.tweak", "width", 15,
2927                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2928                                                               1, 100, 1.0, 10.0,
2929                                                               labels, values, G_N_ELEMENTS(labels),
2930                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2931         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2932         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2933     }
2936     {
2937         /* Force */
2938         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2939         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2940         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2941                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
2942                                                               "tools.tweak", "force", 20,
2943                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2944                                                               1, 100, 1.0, 10.0,
2945                                                               labels, values, G_N_ELEMENTS(labels),
2946                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2947         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2948         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2949     }
2951     /* Mode */
2952     {
2953         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2955         GtkTreeIter iter;
2956         gtk_list_store_append( model, &iter );
2957         gtk_list_store_set( model, &iter,
2958                             0, _("Push mode"),
2959                             1, _("Push parts of paths in any direction"),
2960                             2, "tweak_push_mode",
2961                             -1 );
2963         gtk_list_store_append( model, &iter );
2964         gtk_list_store_set( model, &iter,
2965                             0, _("Shrink mode"),
2966                             1, _("Shrink (inset) parts of paths"),
2967                             2, "tweak_shrink_mode",
2968                             -1 );
2970         gtk_list_store_append( model, &iter );
2971         gtk_list_store_set( model, &iter,
2972                             0, _("Grow mode"),
2973                             1, _("Grow (outset) parts of paths"),
2974                             2, "tweak_grow_mode",
2975                             -1 );
2977         gtk_list_store_append( model, &iter );
2978         gtk_list_store_set( model, &iter,
2979                             0, _("Attract mode"),
2980                             1, _("Attract parts of paths towards cursor"),
2981                             2, "tweak_attract_mode",
2982                             -1 );
2984         gtk_list_store_append( model, &iter );
2985         gtk_list_store_set( model, &iter,
2986                             0, _("Repel mode"),
2987                             1, _("Repel parts of paths from cursor"),
2988                             2, "tweak_repel_mode",
2989                             -1 );
2991         gtk_list_store_append( model, &iter );
2992         gtk_list_store_set( model, &iter,
2993                             0, _("Roughen mode"),
2994                             1, _("Roughen parts of paths"),
2995                             2, "tweak_roughen_mode",
2996                             -1 );
2998         gtk_list_store_append( model, &iter );
2999         gtk_list_store_set( model, &iter,
3000                             0, _("Color paint mode"),
3001                             1, _("Paint the tool's color upon selected objects"),
3002                             2, "tweak_colorpaint_mode",
3003                             -1 );
3005         gtk_list_store_append( model, &iter );
3006         gtk_list_store_set( model, &iter,
3007                             0, _("Color jitter mode"),
3008                             1, _("Jitter the colors of selected objects"),
3009                             2, "tweak_colorjitter_mode",
3010                             -1 );
3012         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3013         g_object_set( act, "short_label", _("Mode:"), NULL );
3014         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3015         g_object_set_data( holder, "mode_action", act );
3017         ege_select_one_action_set_appearance( act, "full" );
3018         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3019         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3020         ege_select_one_action_set_icon_column( act, 2 );
3021         ege_select_one_action_set_tooltip_column( act, 1  );
3023         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3024         ege_select_one_action_set_active( act, mode );
3025         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3027         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3028     }
3030     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3032     {
3033         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3034         ege_output_action_set_use_markup( act, TRUE );
3035         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3036         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3037             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3038         g_object_set_data( holder, "tweak_channels_label", act);
3039     }
3041     {
3042         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3043                                                       _("Hue"),
3044                                                       _("In color mode, act on objects' hue"),
3045                                                       NULL,
3046                                                       Inkscape::ICON_SIZE_DECORATION );
3047         g_object_set( act, "short_label", _("H"), NULL );
3048         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3049         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3050         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3051         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3052             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3053         g_object_set_data( holder, "tweak_doh", act);
3054     }
3055     {
3056         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3057                                                       _("Saturation"),
3058                                                       _("In color mode, act on objects' saturation"),
3059                                                       NULL,
3060                                                       Inkscape::ICON_SIZE_DECORATION );
3061         g_object_set( act, "short_label", _("S"), NULL );
3062         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3063         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3064         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3065         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3066             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3067         g_object_set_data( holder, "tweak_dos", act );
3068     }
3069     {
3070         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3071                                                       _("Lightness"),
3072                                                       _("In color mode, act on objects' lightness"),
3073                                                       NULL,
3074                                                       Inkscape::ICON_SIZE_DECORATION );
3075         g_object_set( act, "short_label", _("L"), NULL );
3076         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3077         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3078         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3079         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3080             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3081         g_object_set_data( holder, "tweak_dol", act );
3082     }
3083     {
3084         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3085                                                       _("Opacity"),
3086                                                       _("In color mode, act on objects' opacity"),
3087                                                       NULL,
3088                                                       Inkscape::ICON_SIZE_DECORATION );
3089         g_object_set( act, "short_label", _("O"), NULL );
3090         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3091         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3092         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3093         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3094             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3095         g_object_set_data( holder, "tweak_doo", act );
3096     }
3098     {   /* Fidelity */
3099         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3100         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3101         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3102                                                               _("Fidelity"), _("Fidelity:"),
3103                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3104                                                               "tools.tweak", "fidelity", 50,
3105                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3106                                                               1, 100, 1.0, 10.0,
3107                                                               labels, values, G_N_ELEMENTS(labels),
3108                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3109         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3110         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3111         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3112             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3113         g_object_set_data( holder, "tweak_fidelity", eact );
3114     }
3117     /* Use Pressure button */
3118     {
3119         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3120                                                       _("Pressure"),
3121                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3122                                                       "use_pressure",
3123                                                       Inkscape::ICON_SIZE_DECORATION );
3124         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3125         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3126         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3127     }
3132 //########################
3133 //##     Calligraphy    ##
3134 //########################
3136 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3138     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3141 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3143     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3146 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3148     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3151 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3153     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3156 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3158     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3161 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3163     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3166 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3168     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3171 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3173     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3176 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3178     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3181 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3183     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3186 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3188     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3190     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3193 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3195     // FIXME: make defaults settable via Inkscape Options
3196     struct KeyValue {
3197         char const *key;
3198         double value;
3199     } const key_values[] = {
3200         {"mass", 0.02},
3201         {"wiggle", 0.0},
3202         {"angle", 30.0},
3203         {"width", 15},
3204         {"thinning", 0.1},
3205         {"tremor", 0.0},
3206         {"flatness", 0.9},
3207         {"cap_rounding", 0.0}
3208     };
3210     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3211         KeyValue const &kv = key_values[i];
3212         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3213         if ( adj ) {
3214             gtk_adjustment_set_value(adj, kv.value);
3215         }
3216     }
3220 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3222     {
3223         EgeAdjustmentAction* calligraphy_angle = 0;
3225         {
3226         /* Width */
3227         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3228         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3229         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3230                                                               _("Pen Width"), _("Width:"),
3231                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3232                                                               "tools.calligraphic", "width", 15,
3233                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3234                                                               1, 100, 1.0, 10.0,
3235                                                               labels, values, G_N_ELEMENTS(labels),
3236                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3237         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3238         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3239         }
3241         {
3242         /* Thinning */
3243             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3244             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3245         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3246                                                               _("Stroke Thinning"), _("Thinning:"),
3247                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3248                                                               "tools.calligraphic", "thinning", 0.1,
3249                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3250                                                               -1.0, 1.0, 0.01, 0.1,
3251                                                               labels, values, G_N_ELEMENTS(labels),
3252                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3253         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3254         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3255         }
3257         {
3258         /* Angle */
3259         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3260         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3261         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3262                                                               _("Pen Angle"), _("Angle:"),
3263                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3264                                                               "tools.calligraphic", "angle", 30,
3265                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3266                                                               -90.0, 90.0, 1.0, 10.0,
3267                                                               labels, values, G_N_ELEMENTS(labels),
3268                                                               sp_ddc_angle_value_changed, 1, 0 );
3269         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3270         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3271         calligraphy_angle = eact;
3272         }
3274         {
3275         /* Fixation */
3276             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3277         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3278         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3279                                                               _("Fixation"), _("Fixation:"),
3280                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3281                                                               "tools.calligraphic", "flatness", 0.9,
3282                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3283                                                               0.0, 1.0, 0.01, 0.1,
3284                                                               labels, values, G_N_ELEMENTS(labels),
3285                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3286         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3287         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3288         }
3290         {
3291         /* Cap Rounding */
3292             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3293         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3294         // TRANSLATORS: "cap" means "end" (both start and finish) here
3295         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3296                                                               _("Cap rounding"), _("Caps:"),
3297                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3298                                                               "tools.calligraphic", "cap_rounding", 0.0,
3299                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3300                                                               0.0, 5.0, 0.01, 0.1,
3301                                                               labels, values, G_N_ELEMENTS(labels),
3302                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3303         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3304         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3305         }
3307         {
3308         /* Tremor */
3309             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3310         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3311         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3312                                                               _("Stroke Tremor"), _("Tremor:"),
3313                                                               _("Increase to make strokes rugged and trembling"),
3314                                                               "tools.calligraphic", "tremor", 0.0,
3315                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3316                                                               0.0, 1.0, 0.01, 0.1,
3317                                                               labels, values, G_N_ELEMENTS(labels),
3318                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3320         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3321         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3322         }
3324         {
3325         /* Wiggle */
3326         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3327         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3328         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3329                                                               _("Pen Wiggle"), _("Wiggle:"),
3330                                                               _("Increase to make the pen waver and wiggle"),
3331                                                               "tools.calligraphic", "wiggle", 0.0,
3332                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3333                                                               0.0, 1.0, 0.01, 0.1,
3334                                                               labels, values, G_N_ELEMENTS(labels),
3335                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3336         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3337         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3338         }
3340         {
3341         /* Mass */
3342             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3343         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3344         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3345                                                               _("Pen Mass"), _("Mass:"),
3346                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3347                                                               "tools.calligraphic", "mass", 0.02,
3348                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3349                                                               0.0, 1.0, 0.01, 0.1,
3350                                                               labels, values, G_N_ELEMENTS(labels),
3351                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3352         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3353         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3354         }
3357         /* Trace Background button */
3358         {
3359             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3360                                                           _("Trace Background"),
3361                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3362                                                           "trace_background",
3363                                                           Inkscape::ICON_SIZE_DECORATION );
3364             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3365             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3366             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3367         }
3369         /* Use Pressure button */
3370         {
3371             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3372                                                           _("Pressure"),
3373                                                           _("Use the pressure of the input device to alter the width of the pen"),
3374                                                           "use_pressure",
3375                                                           Inkscape::ICON_SIZE_DECORATION );
3376             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3377             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3378             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3379         }
3381         /* Use Tilt button */
3382         {
3383             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3384                                                           _("Tilt"),
3385                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3386                                                           "use_tilt",
3387                                                           Inkscape::ICON_SIZE_DECORATION );
3388             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3389             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3390             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3391             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3392         }
3394         /* Reset */
3395         {
3396             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3397                                              _("Defaults"),
3398                                              _("Reset all parameters to defaults"),
3399                                              GTK_STOCK_CLEAR );
3400             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3401             gtk_action_group_add_action( mainActions, act );
3402             gtk_action_set_sensitive( act, TRUE );
3403         }
3404     }
3408 //########################
3409 //##    Circle / Arc    ##
3410 //########################
3412 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3414     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3415     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3417     if (v1 == 0 && v2 == 0) {
3418         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3419             gtk_action_set_sensitive( ocb, FALSE );
3420             gtk_action_set_sensitive( make_whole, FALSE );
3421         }
3422     } else {
3423         gtk_action_set_sensitive( ocb, TRUE );
3424         gtk_action_set_sensitive( make_whole, TRUE );
3425     }
3428 static void
3429 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3431     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3433     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3434         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3435     }
3437     // quit if run by the attr_changed listener
3438     if (g_object_get_data( tbl, "freeze" )) {
3439         return;
3440     }
3442     // in turn, prevent listener from responding
3443     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3445     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3447     bool modmade = false;
3448     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3449          items != NULL;
3450          items = items->next)
3451     {
3452         SPItem *item = SP_ITEM(items->data);
3454         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3456             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3457             SPArc *arc = SP_ARC(item);
3459             if (!strcmp(value_name, "start"))
3460                 ge->start = (adj->value * M_PI)/ 180;
3461             else
3462                 ge->end = (adj->value * M_PI)/ 180;
3464             sp_genericellipse_normalize(ge);
3465             ((SPObject *)arc)->updateRepr();
3466             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3468             modmade = true;
3469         }
3470     }
3472     g_free(namespaced_name);
3474     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3476     sp_arctb_sensitivize( tbl, adj->value, other->value );
3478     if (modmade) {
3479         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3480                                    _("Arc: Change start/end"));
3481     }
3483     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3487 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3489     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3492 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3494     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3497 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3499     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3500     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3501         if ( ege_select_one_action_get_active( act ) != 0 ) {
3502             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3503         } else {
3504             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3505         }
3506     }
3508     // quit if run by the attr_changed listener
3509     if (g_object_get_data( tbl, "freeze" )) {
3510         return;
3511     }
3513     // in turn, prevent listener from responding
3514     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3516     bool modmade = false;
3518     if ( ege_select_one_action_get_active(act) != 0 ) {
3519         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3520              items != NULL;
3521              items = items->next)
3522         {
3523             if (SP_IS_ARC((SPItem *) items->data)) {
3524                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3525                 repr->setAttribute("sodipodi:open", "true");
3526                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3527                 modmade = true;
3528             }
3529         }
3530     } else {
3531         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3532              items != NULL;
3533              items = items->next)
3534         {
3535             if (SP_IS_ARC((SPItem *) items->data))    {
3536                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3537                 repr->setAttribute("sodipodi:open", NULL);
3538                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3539                 modmade = true;
3540             }
3541         }
3542     }
3544     if (modmade) {
3545         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3546                                    _("Arc: Change open/closed"));
3547     }
3549     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3552 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3554     GtkAdjustment *adj;
3555     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3556     gtk_adjustment_set_value(adj, 0.0);
3557     gtk_adjustment_value_changed(adj);
3559     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3560     gtk_adjustment_set_value(adj, 0.0);
3561     gtk_adjustment_value_changed(adj);
3563     spinbutton_defocus( GTK_OBJECT(obj) );
3566 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3567                                       gchar const */*old_value*/, gchar const */*new_value*/,
3568                                       bool /*is_interactive*/, gpointer data)
3570     GObject *tbl = G_OBJECT(data);
3572     // quit if run by the _changed callbacks
3573     if (g_object_get_data( tbl, "freeze" )) {
3574         return;
3575     }
3577     // in turn, prevent callbacks from responding
3578     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3580     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3581     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3583     GtkAdjustment *adj1,*adj2;
3584     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3585     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3586     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3587     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3589     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3591     char const *openstr = NULL;
3592     openstr = repr->attribute("sodipodi:open");
3593     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3595     if (openstr) {
3596         ege_select_one_action_set_active( ocb, 1 );
3597     } else {
3598         ege_select_one_action_set_active( ocb, 0 );
3599     }
3601     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3604 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3605     NULL, /* child_added */
3606     NULL, /* child_removed */
3607     arc_tb_event_attr_changed,
3608     NULL, /* content_changed */
3609     NULL  /* order_changed */
3610 };
3613 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3615     int n_selected = 0;
3616     Inkscape::XML::Node *repr = NULL;
3618     purge_repr_listener( tbl, tbl );
3620     for (GSList const *items = selection->itemList();
3621          items != NULL;
3622          items = items->next)
3623     {
3624         if (SP_IS_ARC((SPItem *) items->data)) {
3625             n_selected++;
3626             repr = SP_OBJECT_REPR((SPItem *) items->data);
3627         }
3628     }
3630     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3632     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3633     if (n_selected == 0) {
3634         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3635     } else if (n_selected == 1) {
3636         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3637         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3639         if (repr) {
3640             g_object_set_data( tbl, "repr", repr );
3641             Inkscape::GC::anchor(repr);
3642             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3643             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3644         }
3645     } else {
3646         // FIXME: implement averaging of all parameters for multiple selected
3647         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3648         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3649         sp_arctb_sensitivize( tbl, 1, 0 );
3650     }
3654 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3656     EgeAdjustmentAction* eact = 0;
3659     {
3660         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3661         ege_output_action_set_use_markup( act, TRUE );
3662         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3663         g_object_set_data( holder, "mode_action", act );
3664     }
3666     /* Start */
3667     {
3668         eact = create_adjustment_action( "ArcStartAction",
3669                                          _("Start"), _("Start:"),
3670                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3671                                          "tools.shapes.arc", "start", 0.0,
3672                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3673                                          -360.0, 360.0, 1.0, 10.0,
3674                                          0, 0, 0,
3675                                          sp_arctb_start_value_changed);
3676         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3677     }
3679     /* End */
3680     {
3681         eact = create_adjustment_action( "ArcEndAction",
3682                                          _("End"), _("End:"),
3683                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3684                                          "tools.shapes.arc", "end", 0.0,
3685                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3686                                          -360.0, 360.0, 1.0, 10.0,
3687                                          0, 0, 0,
3688                                          sp_arctb_end_value_changed);
3689         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3690     }
3692     /* Segments / Pie checkbox */
3693     {
3694         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3696         GtkTreeIter iter;
3697         gtk_list_store_append( model, &iter );
3698         gtk_list_store_set( model, &iter,
3699                             0, _("Closed arc"),
3700                             1, _("Switch to segment (closed shape with two radii)"),
3701                             2, "circle_closed_arc",
3702                             -1 );
3704         gtk_list_store_append( model, &iter );
3705         gtk_list_store_set( model, &iter,
3706                             0, _("Open Arc"),
3707                             1, _("Switch to arc (unclosed shape)"),
3708                             2, "circle_open_arc",
3709                             -1 );
3711         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3712         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3713         g_object_set_data( holder, "open_action", act );
3715         ege_select_one_action_set_appearance( act, "full" );
3716         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3717         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3718         ege_select_one_action_set_icon_column( act, 2 );
3719         ege_select_one_action_set_tooltip_column( act, 1  );
3721         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3722         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3723         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3724         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3725     }
3727     /* Make Whole */
3728     {
3729         InkAction* inky = ink_action_new( "ArcResetAction",
3730                                           _("Make whole"),
3731                                           _("Make the shape a whole ellipse, not arc or segment"),
3732                                           "reset_circle",
3733                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3734         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3735         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3736         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3737         g_object_set_data( holder, "make_whole", inky );
3738     }
3740     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3741     // sensitivize make whole and open checkbox
3742     {
3743         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3744         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3745         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3746     }
3749     sigc::connection *connection = new sigc::connection(
3750         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3751         );
3752     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3753     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3759 // toggle button callbacks and updaters
3761 //########################
3762 //##      Dropper       ##
3763 //########################
3765 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3766     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3767     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3768     if ( set_action ) {
3769         if ( gtk_toggle_action_get_active( act ) ) {
3770             gtk_action_set_sensitive( set_action, TRUE );
3771         } else {
3772             gtk_action_set_sensitive( set_action, FALSE );
3773         }
3774     }
3776     spinbutton_defocus(GTK_OBJECT(tbl));
3779 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3780     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3781     spinbutton_defocus(GTK_OBJECT(tbl));
3785 /**
3786  * Dropper auxiliary toolbar construction and setup.
3787  *
3788  * TODO: Would like to add swatch of current color.
3789  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3790  *       can drag and drop places. Will provide a nice mixing palette.
3791  */
3792 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3794     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3796     {
3797         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3798                                                       _("Pick alpha"),
3799                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3800                                                       "color_alpha_get",
3801                                                       Inkscape::ICON_SIZE_DECORATION );
3802         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3803         g_object_set_data( holder, "pick_action", act );
3804         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3805         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3806     }
3808     {
3809         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3810                                                       _("Set alpha"),
3811                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3812                                                       "color_alpha_set",
3813                                                       Inkscape::ICON_SIZE_DECORATION );
3814         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3815         g_object_set_data( holder, "set_action", act );
3816         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3817         // make sure it's disabled if we're not picking alpha
3818         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3819         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3820     }
3824 //########################
3825 //##    Text Toolbox    ##
3826 //########################
3827 /*
3828 static void
3829 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3831     //Call back for letter sizing spinbutton
3834 static void
3835 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3837     //Call back for line height spinbutton
3840 static void
3841 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3843     //Call back for horizontal kerning spinbutton
3846 static void
3847 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3849     //Call back for vertical kerning spinbutton
3852 static void
3853 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3855     //Call back for letter rotation spinbutton
3856 }*/
3858 namespace {
3860 bool visible = false;
3862 void
3863 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3865     SPStyle *query =
3866         sp_style_new (SP_ACTIVE_DOCUMENT);
3868     int result_family =
3869         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3871     int result_style =
3872         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3874     int result_numbers =
3875         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3877     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3879     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3880     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3881     {
3882         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3884         if (repr)
3885         {
3886             sp_style_read_from_repr (query, repr);
3887         }
3888         else
3889         {
3890             return;
3891         }
3892     }
3894     if (query->text)
3895     {
3896         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3897             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3898             gtk_entry_set_text (GTK_ENTRY (entry), "");
3900         } else if (query->text->font_family.value) {
3902             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3903             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3905             Gtk::TreePath path;
3906             try {
3907                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3908             } catch (...) {
3909                 return;
3910             }
3912             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3913             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3915             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3917             gtk_tree_selection_select_path (tselection, path.gobj());
3918             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3920             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3921         }
3923         //Size
3924         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3925         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3926         g_object_set_data (tbl, "size-block", gpointer(1));
3927         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3928         g_object_set_data (tbl, "size-block", gpointer(0));
3929         free (str);
3931         //Anchor
3932         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3933         {
3934             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3935             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3936             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3937             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3938         }
3939         else
3940         {
3941             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3942             {
3943                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3944                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3945                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3946                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3947             }
3948             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3949             {
3950                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3951                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3952                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3953                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3954             }
3955             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3956             {
3957                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3958                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3959                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3960                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3961             }
3962         }
3964         //Style
3965         {
3966             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3968             gboolean active = gtk_toggle_button_get_active (button);
3969             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3971             if (active != check)
3972             {
3973                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3974                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3975                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3976             }
3977         }
3979         {
3980             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3982             gboolean active = gtk_toggle_button_get_active (button);
3983             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3985             if (active != check)
3986             {
3987                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3988                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3989                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3990             }
3991         }
3993         //Orientation
3994         //locking both buttons, changing one affect all group (both)
3995         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3996         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3998         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3999         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4001         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4002         {
4003             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4004         }
4005         else
4006         {
4007             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4008         }
4009         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4010         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4011     }
4013     sp_style_unref(query);
4016 void
4017 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4019     sp_text_toolbox_selection_changed (selection, tbl);
4022 void
4023 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4025     sp_text_toolbox_selection_changed (NULL, tbl);
4028 void
4029 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4030                                 GObject             *tbl)
4032     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4033     GtkTreeModel *model = 0;
4034     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4035     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4036     GtkTreeIter   iter;
4037     char         *family = 0;
4039     (void)popdown;
4041     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4042     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4044     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4045         return;
4046     }
4048     gtk_tree_model_get (model, &iter, 0, &family, -1);
4050     if (g_object_get_data (G_OBJECT (selection), "block"))
4051     {
4052         gtk_entry_set_text (GTK_ENTRY (entry), family);
4053         return;
4054     }
4056     gtk_entry_set_text (GTK_ENTRY (entry), family);
4058     SPStyle *query =
4059         sp_style_new (SP_ACTIVE_DOCUMENT);
4061     int result_numbers =
4062         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4064     SPCSSAttr *css = sp_repr_css_attr_new ();
4065     sp_repr_css_set_property (css, "font-family", family);
4067     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4068     if (result_numbers == QUERY_STYLE_NOTHING)
4069     {
4070         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4071         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4072     }
4073     else
4074     {
4075         sp_desktop_set_style (desktop, css, true, true);
4076     }
4078     sp_style_unref(query);
4080     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4081                                    _("Text: Change font family"));
4082     sp_repr_css_attr_unref (css);
4083     free (family);
4084     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4086     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4089 void
4090 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4091                                        GObject      *tbl)
4093     const char *family = gtk_entry_get_text (entry);
4095     try {
4096         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4097         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4098         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4099         gtk_tree_selection_select_path (selection, path.gobj());
4100         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4101         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4102     } catch (...) {
4103         if (family && strlen (family))
4104         {
4105             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4106         }
4107     }
4110 void
4111 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4112                                    gpointer          data)
4114     if (g_object_get_data (G_OBJECT (button), "block")) return;
4115     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4116     int prop = GPOINTER_TO_INT(data);
4118     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4119     SPCSSAttr *css = sp_repr_css_attr_new ();
4121     switch (prop)
4122     {
4123         case 0:
4124         {
4125             sp_repr_css_set_property (css, "text-anchor", "start");
4126             sp_repr_css_set_property (css, "text-align", "start");
4127             break;
4128         }
4129         case 1:
4130         {
4131             sp_repr_css_set_property (css, "text-anchor", "middle");
4132             sp_repr_css_set_property (css, "text-align", "center");
4133             break;
4134         }
4136         case 2:
4137         {
4138             sp_repr_css_set_property (css, "text-anchor", "end");
4139             sp_repr_css_set_property (css, "text-align", "end");
4140             break;
4141         }
4143         case 3:
4144         {
4145             sp_repr_css_set_property (css, "text-anchor", "start");
4146             sp_repr_css_set_property (css, "text-align", "justify");
4147             break;
4148         }
4149     }
4151     SPStyle *query =
4152         sp_style_new (SP_ACTIVE_DOCUMENT);
4153     int result_numbers =
4154         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4156     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4157     if (result_numbers == QUERY_STYLE_NOTHING)
4158     {
4159         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4160     }
4162     sp_style_unref(query);
4164     sp_desktop_set_style (desktop, css, true, true);
4165     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4166                                    _("Text: Change alignment"));
4167     sp_repr_css_attr_unref (css);
4169     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4172 void
4173 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4174                                gpointer          data)
4176     if (g_object_get_data (G_OBJECT (button), "block")) return;
4178     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4179     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4180     int          prop       = GPOINTER_TO_INT(data);
4181     bool         active     = gtk_toggle_button_get_active (button);
4184     switch (prop)
4185     {
4186         case 0:
4187         {
4188             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4189             break;
4190         }
4192         case 1:
4193         {
4194             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4195             break;
4196         }
4197     }
4199     SPStyle *query =
4200         sp_style_new (SP_ACTIVE_DOCUMENT);
4201     int result_numbers =
4202         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4204     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4205     if (result_numbers == QUERY_STYLE_NOTHING)
4206     {
4207         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4208     }
4210     sp_style_unref(query);
4212     sp_desktop_set_style (desktop, css, true, true);
4213     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4214                                    _("Text: Change font style"));
4215     sp_repr_css_attr_unref (css);
4217     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4220 void
4221 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4222                                      gpointer         data)
4224     if (g_object_get_data (G_OBJECT (button), "block")) {
4225         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4226         return;
4227     }
4229     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4230     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4231     int          prop       = GPOINTER_TO_INT(data);
4233     switch (prop)
4234     {
4235         case 0:
4236         {
4237             sp_repr_css_set_property (css, "writing-mode", "lr");
4238             break;
4239         }
4241         case 1:
4242         {
4243             sp_repr_css_set_property (css, "writing-mode", "tb");
4244             break;
4245         }
4246     }
4248     SPStyle *query =
4249         sp_style_new (SP_ACTIVE_DOCUMENT);
4250     int result_numbers =
4251         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4253     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4254     if (result_numbers == QUERY_STYLE_NOTHING)
4255     {
4256         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4257     }
4259     sp_desktop_set_style (desktop, css, true, true);
4260     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4261                                    _("Text: Change orientation"));
4262     sp_repr_css_attr_unref (css);
4264     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4267 gboolean
4268 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4270     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4271     if (!desktop) return FALSE;
4273     switch (get_group0_keyval (event)) {
4274         case GDK_Escape: // defocus
4275             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4276             return TRUE; // I consumed the event
4277             break;
4278         case GDK_Return: // defocus
4279         case GDK_KP_Enter:
4280             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4281             return TRUE; // I consumed the event
4282             break;
4283     }
4284     return FALSE;
4287 gboolean
4288 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4290     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4291     if (!desktop) return FALSE;
4293     switch (get_group0_keyval (event)) {
4294         case GDK_Escape: // defocus
4295             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4296             sp_text_toolbox_selection_changed (NULL, tbl); // update
4297             return TRUE; // I consumed the event
4298             break;
4299     }
4300     return FALSE;
4303 gboolean
4304 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4306     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4307     if (!desktop) return FALSE;
4309     switch (get_group0_keyval (event)) {
4310         case GDK_KP_Enter:
4311         case GDK_Return:
4312         case GDK_Escape: // defocus
4313             gtk_widget_hide (w);
4314             visible = false;
4315             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4316             return TRUE; // I consumed the event
4317             break;
4318     }
4319     return FALSE;
4323 void
4324 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4325                                GObject     *tbl)
4327     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4329     if (g_object_get_data (tbl, "size-block")) return;
4331 #if GTK_CHECK_VERSION(2,6,0)
4332     char *text = gtk_combo_box_get_active_text (cbox);
4333 #else // GTK_CHECK_VERSION(2,6,0)
4334     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
4335     GtkTreeIter iter;
4336     char *text = NULL;
4338     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
4339         gtk_tree_model_get (model, &iter, 0, &text, -1);
4340 #endif // GTK_CHECK_VERSION(2,6,0)
4342     SPCSSAttr *css = sp_repr_css_attr_new ();
4343     sp_repr_css_set_property (css, "font-size", text);
4344     free (text);
4346     SPStyle *query =
4347         sp_style_new (SP_ACTIVE_DOCUMENT);
4348     int result_numbers =
4349         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4351     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4352     if (result_numbers == QUERY_STYLE_NOTHING)
4353     {
4354         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4355     }
4357     sp_style_unref(query);
4359     sp_desktop_set_style (desktop, css, true, true);
4360     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4361                                    _("Text: Change font size"));
4362     sp_repr_css_attr_unref (css);
4365     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4366         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4369 void
4370 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4371                                          GObject            *tbl)
4373     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4374     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4375     int x, y;
4377     if (!visible)
4378     {
4379         gdk_window_get_origin (widget->window, &x, &y);
4380         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4381         gtk_widget_show_all (popdown);
4383         gdk_pointer_grab (widget->window, TRUE,
4384                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4385                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4386                                         GDK_POINTER_MOTION_MASK),
4387                           NULL, NULL, GDK_CURRENT_TIME);
4389         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4391         visible = true;
4392     }
4393     else
4394     {
4395         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4396         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4397         gtk_widget_hide (popdown);
4398         visible = false;
4399     }
4402 gboolean
4403 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4404                                  GdkEventFocus    */*event*/,
4405                                  GObject          */*tbl*/)
4407     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4408     return FALSE;
4411 gboolean
4412 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4413                                    GdkEventFocus    */*event*/,
4414                                    GObject          */*tbl*/)
4416     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4418     gtk_widget_hide (popdown);
4419     visible = false;
4420     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4421     return TRUE;
4424 void
4425 cell_data_func  (GtkTreeViewColumn */*column*/,
4426                  GtkCellRenderer   *cell,
4427                  GtkTreeModel      *tree_model,
4428                  GtkTreeIter       *iter,
4429                  gpointer           /*data*/)
4431     char        *family,
4432         *family_escaped,
4433         *sample_escaped;
4435     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4437     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4439     family_escaped = g_markup_escape_text (family, -1);
4440     sample_escaped = g_markup_escape_text (sample, -1);
4442     std::stringstream markup;
4443     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4444     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4446     free (family);
4447     free (family_escaped);
4448     free (sample_escaped);
4451 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4452     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4453     if (completion) {
4454         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4455         g_object_unref (completion);
4456     }
4459 GtkWidget*
4460 sp_text_toolbox_new (SPDesktop *desktop)
4462     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4464     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4465     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4467     GtkTooltips *tt = gtk_tooltips_new();
4468     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4470     ////////////Family
4471     //Window
4472     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4473     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4475     //Entry
4476     GtkWidget           *entry = gtk_entry_new ();
4477     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4478     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4479     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4480     gtk_entry_completion_set_text_column (completion, 0);
4481     gtk_entry_completion_set_minimum_key_length (completion, 1);
4482     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4483     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4484     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4485     aux_toolbox_space (tbl, 1);
4486     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4487     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4489     //Button
4490     GtkWidget   *button = gtk_button_new ();
4491     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4492     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4494     //Popdown
4495     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4496     GtkWidget           *treeview = gtk_tree_view_new ();
4498     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4499     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4500     gtk_tree_view_column_pack_start (column, cell, FALSE);
4501     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4502     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4503     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4505     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4506     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4507 #if GTK_CHECK_VERSION(2,6,0)
4508     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4509 #endif // GTK_CHECK_VERSION(2,6,0)
4511     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4513     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4514     gtk_container_add (GTK_CONTAINER (sw), treeview);
4516     gtk_container_add (GTK_CONTAINER (window), sw);
4517     gtk_widget_set_size_request (window, 300, 450);
4519     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4520     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4521     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4523     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4525     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4526     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4528     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4529     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4531     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4532     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4533     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4534     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4535     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4537     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4538     aux_toolbox_space (tbl, 1);
4539     GtkWidget *box = gtk_event_box_new ();
4540     gtk_container_add (GTK_CONTAINER (box), image);
4541     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4542     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4543     GtkTooltips *tooltips = gtk_tooltips_new ();
4544     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4545     gtk_widget_hide (GTK_WIDGET (box));
4546     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4548     ////////////Size
4549     const char *sizes[] = {
4550         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4551         "16", "18", "20", "22", "24", "28",
4552         "32", "36", "40", "48", "56", "64", "72", "144"
4553     };
4555     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4556     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4557     gtk_widget_set_size_request (cbox, 80, -1);
4558     aux_toolbox_space (tbl, 1);
4559     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4560     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4561     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4562     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4564     //spacer
4565     aux_toolbox_space (tbl, 4);
4566     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4568     ////////////Text anchor
4569     GtkWidget *group   = gtk_radio_button_new (NULL);
4570     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4571     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4573     // left
4574     GtkWidget *rbutton = group;
4575     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4576     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4577     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4579     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4580     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4581     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4582     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4584     // center
4585     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4586     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4587     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4588     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4590     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4591     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4592     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4593     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4595     // right
4596     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4597     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4598     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4599     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4601     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4602     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4603     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4604     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4606     // fill
4607     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4608     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4609     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4610     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4612     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4613     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4614     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4615     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4617     aux_toolbox_space (tbl, 1);
4618     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4620     //spacer
4621     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4623     ////////////Text style
4624     row = gtk_hbox_new (FALSE, 4);
4626     // bold
4627     rbutton = gtk_toggle_button_new ();
4628     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4629     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4630     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4631     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4633     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4634     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4635     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4637     // italic
4638     rbutton = gtk_toggle_button_new ();
4639     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4640     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4641     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4642     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4644     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4645     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4646     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4648     aux_toolbox_space (tbl, 1);
4649     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4651     //spacer
4652     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4654     ////////////Text orientation
4655     group   = gtk_radio_button_new (NULL);
4656     row     = gtk_hbox_new (FALSE, 4);
4657     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4659     // horizontal
4660     rbutton = group;
4661     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4662     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4663     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4664     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4666     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4667     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4668     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4670     // vertical
4671     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4672     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4673     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4674     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4675     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4677     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4678     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4679     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4680     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4683     //watch selection
4684     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4686     sigc::connection *c_selection_changed =
4687         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4688                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4689     pool->add_connection ("selection-changed", c_selection_changed);
4691     sigc::connection *c_selection_modified =
4692         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4693                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4694     pool->add_connection ("selection-modified", c_selection_modified);
4696     sigc::connection *c_subselection_changed =
4697         new sigc::connection (desktop->connectToolSubselectionChanged
4698                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4699     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4701     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4704 #if 0
4705     // horizontal
4706     {
4707         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4708         GtkWidget *b = group = gtk_radio_button_new (NULL);
4709         gtk_container_add (GTK_CONTAINER (b), px);
4710         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4711         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4712         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4713         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4714     }
4716     // vertical
4717     {
4718         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4719         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4720         gtk_container_add (GTK_CONTAINER (b), px);
4721         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4722         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4723         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4724         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4725     }
4727     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4729     // letter spacing
4730     {
4731         {
4732             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4733             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4734             gtk_container_add (GTK_CONTAINER (hb), image);
4735             gtk_widget_show(image);
4736             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4737         }
4739         {
4740             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between letters"),
4741                                              "tools.text", "letter_spacing", 0.0,
4742                                              us, tbl, FALSE, NULL,
4743                                              -1000.0, 1000.0, 0.1, 0.1,
4744                                              sp_text_letter_changed, 0.1, 1);
4745             gtk_widget_set_size_request (hb, 45, 6);
4746             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4747         }
4748     }
4750     // line spacing
4751     {
4752         {
4753             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4754             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4755             gtk_container_add (GTK_CONTAINER (hb), image);
4756             gtk_widget_show(image);
4757             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4758         }
4760         {
4761             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between lines"),
4762                                              "tools.text", "line_spacing", 0,
4763                                              us, tbl, FALSE, NULL,
4764                                              -1000.0, 1000.0, 0.1, 0.1,
4765                                              sp_text_line_changed, 0.1, 1);
4766             gtk_widget_set_size_request (hb, 45, 0);
4767             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4768         }
4769     }
4771     {
4772         // horizontal kerning/vertical kerning units menu: create
4773         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4774         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4775         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4777         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4779         // horizontal kerning
4780         {
4781             {
4782                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4783                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4784                 gtk_container_add (GTK_CONTAINER (hb), image);
4785                 gtk_widget_show(image);
4786                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4787             }
4789             {
4790                 GtkWidget *hb = sp_tb_spinbutton((""), _("Horizontal kerning"),
4791                                                  "tools.text", "horizontal_kerning", 0,
4792                                                  us, tbl, FALSE, NULL,
4793                                                  -100.00, 100.00, 0.01, 0.1,
4794                                                  sp_text_horiz_kern_changed);
4795                 gtk_widget_set_size_request (hb, 45, 0);
4796                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4797             }
4798         }
4800         // vertical kerning
4801         {
4802             {
4803                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4804                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4805                 gtk_container_add (GTK_CONTAINER (hb), image);
4806                 gtk_widget_show(image);
4807                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4808             }
4810             {
4811                 GtkWidget *hb = sp_tb_spinbutton((""), _("Vertical kerning"),
4812                                                  "tools.text", "vertical_kerning", 0,
4813                                                  us, tbl, FALSE, NULL,
4814                                                  -100.00, 100.00, 0.01, 0.1,
4815                                                  sp_text_vert_kern_changed);
4816                 gtk_widget_set_size_request (hb, 45, 0);
4817                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4818             }
4819         }
4821         // add the units menu
4822         gtk_widget_show(us);
4823         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4824         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4825     }
4827     // letter rotation
4828     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4829     {
4830         {
4831             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4832             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4833             gtk_container_add (GTK_CONTAINER (hb), image);
4834             gtk_widget_show(image);
4835             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4836         }
4837         {
4838             GtkWidget *hb = sp_tb_spinbutton((""), _("Letter rotation"),
4839                                              "tools.text", "letter_rotation", 0,
4840                                              us, tbl, FALSE, NULL,
4841                                              -180.0, 180.0, 0.1, 0.1,
4842                                              sp_text_letter_rotation_changed, 0.1, 1);
4843             gtk_widget_set_size_request (hb, 45, 0);
4844             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4845         }
4846         // rotation degree label
4847         {
4848             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4849             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4850         }
4851     }
4853     // Remove Manual Kerns
4854     {
4855         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4856         GtkWidget *button = gtk_button_new ();
4857         gtk_container_add (GTK_CONTAINER (button), px);
4858         gtk_widget_show(button);
4859         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4860         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4861         gtk_widget_set_sensitive(button, TRUE);
4862         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4863     }
4864 #endif
4866     gtk_widget_show_all (tbl);
4867     return tbl;
4869 } // end of sp_text_toolbox_new()
4871 }//<unnamed> namespace
4874 //#########################
4875 //##      Connector      ##
4876 //#########################
4878 static void sp_connector_path_set_avoid(void)
4880     cc_selection_set_avoid(true);
4884 static void sp_connector_path_set_ignore(void)
4886     cc_selection_set_avoid(false);
4891 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4893     // quit if run by the _changed callbacks
4894     if (g_object_get_data( tbl, "freeze" )) {
4895         return;
4896     }
4898     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4899     SPDocument *doc = sp_desktop_document(desktop);
4901     if (!sp_document_get_undo_sensitive(doc))
4902     {
4903         return;
4904     }
4906     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4908     if ( repr->attribute("inkscape:connector-spacing") ) {
4909         gdouble priorValue = gtk_adjustment_get_value(adj);
4910         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4911         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4912             return;
4913         }
4914     } else if ( adj->value == defaultConnSpacing ) {
4915         return;
4916     }
4918     // in turn, prevent callbacks from responding
4919     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4921     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4922     SP_OBJECT(desktop->namedview)->updateRepr();
4924     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4925     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4926         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4927         NR::Matrix m = NR::identity();
4928         avoid_item_move(&m, item);
4929     }
4931     if (items) {
4932         g_slist_free(items);
4933     }
4935     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4936             _("Change connector spacing"));
4938     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4940     spinbutton_defocus(GTK_OBJECT(tbl));
4943 static void sp_connector_graph_layout(void)
4945     if (!SP_ACTIVE_DESKTOP) return;
4947     // hack for clones, see comment in align-and-distribute.cpp
4948     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4949     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4951     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4953     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4955     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4958 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4960     if ( gtk_toggle_action_get_active( act ) ) {
4961         prefs_set_string_attribute("tools.connector", "directedlayout",
4962                 "true");
4963     } else {
4964         prefs_set_string_attribute("tools.connector", "directedlayout",
4965                 "false");
4966     }
4969 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4971     if ( gtk_toggle_action_get_active( act ) ) {
4972         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4973                 "true");
4974     } else {
4975         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4976                 "false");
4977     }
4981 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4983     prefs_set_double_attribute("tools.connector", "length", adj->value);
4984     spinbutton_defocus(GTK_OBJECT(tbl));
4987 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4988                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
4989                                             bool /*is_interactive*/, gpointer data)
4991     GtkWidget *tbl = GTK_WIDGET(data);
4993     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4994         return;
4995     }
4996     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4997         return;
4998     }
5000     GtkAdjustment *adj = (GtkAdjustment*)
5001             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5002     gdouble spacing = defaultConnSpacing;
5003     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5005     gtk_adjustment_set_value(adj, spacing);
5009 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5010     NULL, /* child_added */
5011     NULL, /* child_removed */
5012     connector_tb_event_attr_changed,
5013     NULL, /* content_changed */
5014     NULL  /* order_changed */
5015 };
5018 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5020     {
5021         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5022                                           _("Avoid"),
5023                                           _("Make connectors avoid selected objects"),
5024                                           "connector_avoid",
5025                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5026         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5027         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5028     }
5030     {
5031         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5032                                           _("Ignore"),
5033                                           _("Make connectors ignore selected objects"),
5034                                           "connector_ignore",
5035                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5036         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5037         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5038     }
5040     EgeAdjustmentAction* eact = 0;
5042     // Spacing spinbox
5043     eact = create_adjustment_action( "ConnectorSpacingAction",
5044                                      _("Connector Spacing"), _("Spacing:"),
5045                                      _("The amount of space left around objects by auto-routing connectors"),
5046                                      "tools.connector", "spacing", defaultConnSpacing,
5047                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5048                                      0, 100, 1.0, 10.0,
5049                                      0, 0, 0,
5050                                      connector_spacing_changed, 1, 0 );
5051     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5053     // Graph (connector network) layout
5054     {
5055         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5056                                           _("Graph"),
5057                                           _("Nicely arrange selected connector network"),
5058                                           "graph_layout",
5059                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5060         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5061         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5062     }
5064     // Default connector length spinbox
5065     eact = create_adjustment_action( "ConnectorLengthAction",
5066                                      _("Connector Length"), _("Length:"),
5067                                      _("Ideal length for connectors when layout is applied"),
5068                                      "tools.connector", "length", 100,
5069                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5070                                      10, 1000, 10.0, 100.0,
5071                                      0, 0, 0,
5072                                      connector_length_changed, 1, 0 );
5073     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5076     // Directed edges toggle button
5077     {
5078         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5079                                                       _("Downwards"),
5080                                                       _("Make connectors with end-markers (arrows) point downwards"),
5081                                                       "directed_graph",
5082                                                       Inkscape::ICON_SIZE_DECORATION );
5083         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5085         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5086         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5087                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5089         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5090     }
5092     // Avoid overlaps toggle button
5093     {
5094         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5095                                                       _("Remove overlaps"),
5096                                                       _("Do not allow overlapping shapes"),
5097                                                       "remove_overlaps",
5098                                                       Inkscape::ICON_SIZE_DECORATION );
5099         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5101         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5102         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5103                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5105         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5106     }
5108     // Code to watch for changes to the connector-spacing attribute in
5109     // the XML.
5110     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5111     g_assert(repr != NULL);
5113     purge_repr_listener( holder, holder );
5115     if (repr) {
5116         g_object_set_data( holder, "repr", repr );
5117         Inkscape::GC::anchor(repr);
5118         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5119         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5120     }
5121 } // end of sp_connector_toolbox_prep()
5124 //#########################
5125 //##     Paintbucket     ##
5126 //#########################
5128 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5130     gint channels = ege_select_one_action_get_active( act );
5131     flood_channels_set_channels( channels );
5134 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5136     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5139 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5141     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5144 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5146     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5147     SPUnit const *unit = tracker->getActiveUnit();
5149     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5151     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5154 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5156     // FIXME: make defaults settable via Inkscape Options
5157     struct KeyValue {
5158         char const *key;
5159         double value;
5160     } const key_values[] = {
5161         {"threshold", 15},
5162         {"offset", 0.0}
5163     };
5165     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5166         KeyValue const &kv = key_values[i];
5167         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5168         if ( adj ) {
5169             gtk_adjustment_set_value(adj, kv.value);
5170         }
5171     }
5173     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5174     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5175     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5176     ege_select_one_action_set_active( autogap_action, 0 );
5179 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5181     EgeAdjustmentAction* eact = 0;
5183     {
5184         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5186         GList* items = 0;
5187         gint count = 0;
5188         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5189         {
5190             GtkTreeIter iter;
5191             gtk_list_store_append( model, &iter );
5192             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5193             count++;
5194         }
5195         g_list_free( items );
5196         items = 0;
5197         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5198         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5199         ege_select_one_action_set_appearance( act1, "compact" );
5200         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5201         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5202         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5203         g_object_set_data( holder, "channels_action", act1 );
5204     }
5206     // Spacing spinbox
5207     {
5208         eact = create_adjustment_action(
5209             "ThresholdAction",
5210             _("Fill Threshold"), _("Threshold:"),
5211             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5212             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5213             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5214             0, 0, 0,
5215             paintbucket_threshold_changed, 1, 0 );
5217         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5218     }
5220     // Create the units menu.
5221     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5222     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5223     g_object_set_data( holder, "tracker", tracker );
5224     {
5225         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5226         gtk_action_group_add_action( mainActions, act );
5227     }
5229     // Offset spinbox
5230     {
5231         eact = create_adjustment_action(
5232             "OffsetAction",
5233             _("Grow/shrink by"), _("Grow/shrink by:"),
5234             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5235             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5236             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5237             0, 0, 0,
5238             paintbucket_offset_changed, 1, 2);
5239         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5241         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5242     }
5244     /* Auto Gap */
5245     {
5246         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5248         GList* items = 0;
5249         gint count = 0;
5250         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5251         {
5252             GtkTreeIter iter;
5253             gtk_list_store_append( model, &iter );
5254             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5255             count++;
5256         }
5257         g_list_free( items );
5258         items = 0;
5259         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5260         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5261         ege_select_one_action_set_appearance( act2, "compact" );
5262         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5263         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5264         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5265         g_object_set_data( holder, "autogap_action", act2 );
5266     }
5268     /* Reset */
5269     {
5270         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5271                                           _("Defaults"),
5272                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5273                                           GTK_STOCK_CLEAR );
5274         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5275         gtk_action_group_add_action( mainActions, act );
5276         gtk_action_set_sensitive( act, TRUE );
5277     }
5281 /*
5282   Local Variables:
5283   mode:c++
5284   c-file-style:"stroustrup"
5285   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5286   indent-tabs-mode:nil
5287   fill-column:99
5288   End:
5289 */
5290 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :