Code

3c35d0cfecc767aff62edda8e5ee61dabac172bc
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <gtkmm.h>
31 #include <gtk/gtk.h>
32 #include <iostream>
33 #include <sstream>
35 #include "widgets/button.h"
36 #include "widgets/widget-sizes.h"
37 #include "widgets/spw-utilities.h"
38 #include "widgets/spinbutton-events.h"
39 #include "dialogs/text-edit.h"
41 #include "ui/widget/style-swatch.h"
43 #include "prefs-utils.h"
44 #include "verbs.h"
45 #include "sp-namedview.h"
46 #include "desktop.h"
47 #include "desktop-handles.h"
48 #include "xml/repr.h"
49 #include "xml/node-event-vector.h"
50 #include <glibmm/i18n.h>
51 #include "helper/unit-menu.h"
52 #include "helper/units.h"
54 #include "inkscape.h"
55 #include "conn-avoid-ref.h"
58 #include "select-toolbar.h"
59 #include "gradient-toolbar.h"
61 #include "connector-context.h"
62 #include "node-context.h"
63 #include "shape-editor.h"
64 #include "tweak-context.h"
65 #include "sp-rect.h"
66 #include "box3d.h"
67 #include "box3d-context.h"
68 #include "sp-star.h"
69 #include "sp-spiral.h"
70 #include "sp-ellipse.h"
71 #include "sp-text.h"
72 #include "sp-flowtext.h"
73 #include "style.h"
74 #include "selection.h"
75 #include "selection-chemistry.h"
76 #include "document-private.h"
77 #include "desktop-style.h"
78 #include "../libnrtype/font-lister.h"
79 #include "../libnrtype/font-instance.h"
80 #include "../connection-pool.h"
81 #include "../prefs-utils.h"
82 #include "../inkscape-stock.h"
83 #include "icon.h"
84 #include "graphlayout/graphlayout.h"
86 #include "mod360.h"
88 #include "toolbox.h"
90 #include "flood-context.h"
92 #include "ink-action.h"
93 #include "ege-adjustment-action.h"
94 #include "ege-output-action.h"
95 #include "ege-select-one-action.h"
96 #include "helper/unit-tracker.h"
98 using Inkscape::UnitTracker;
100 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
101 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
103 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
116 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
122 static struct {
123     gchar const *type_name;
124     gchar const *data_name;
125     sp_verb_t verb;
126     sp_verb_t doubleclick_verb;
127 } const tools[] = {
128     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
129     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
130     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
131     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
132     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
133     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
134     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
135     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
136     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
137     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
138     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
139     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
140     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
141     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
142     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
143     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
144     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
145     { NULL, NULL, 0, 0 }
146 };
148 static struct {
149     gchar const *type_name;
150     gchar const *data_name;
151     GtkWidget *(*create_func)(SPDesktop *desktop);
152     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
153     gchar const *ui_name;
154     gint swatch_verb_id;
155     gchar const *swatch_tool;
156     gchar const *swatch_tip;
157 } const aux_toolboxes[] = {
158     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
159       SP_VERB_INVALID, 0, 0},
160     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
161       SP_VERB_INVALID, 0, 0},
162     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
163       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
164     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
165       SP_VERB_INVALID, 0, 0},
166     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
167       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
168     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
169       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
170     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
171       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
172     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
173       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
174     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
175       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
176     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
177       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
178     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
179       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
180     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
181       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
182     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
183       SP_VERB_INVALID, 0, 0},
184     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
187       SP_VERB_INVALID, 0, 0},
188     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
189       SP_VERB_INVALID, 0, 0},
190     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
191       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
192     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
193 };
196 static gchar const * ui_descr =
197         "<ui>"
198         "  <toolbar name='SelectToolbar'>"
199         "    <toolitem action='EditSelectAll' />"
200         "    <toolitem action='EditSelectAllInAllLayers' />"
201         "    <toolitem action='EditDeselect' />"
202         "    <separator />"
203         "    <toolitem action='ObjectRotate90CCW' />"
204         "    <toolitem action='ObjectRotate90' />"
205         "    <toolitem action='ObjectFlipHorizontally' />"
206         "    <toolitem action='ObjectFlipVertically' />"
207         "    <separator />"
208         "    <toolitem action='SelectionToBack' />"
209         "    <toolitem action='SelectionLower' />"
210         "    <toolitem action='SelectionRaise' />"
211         "    <toolitem action='SelectionToFront' />"
212         "    <separator />"
213         "    <toolitem action='XAction' />"
214         "    <toolitem action='YAction' />"
215         "    <toolitem action='WidthAction' />"
216         "    <toolitem action='LockAction' />"
217         "    <toolitem action='HeightAction' />"
218         "    <toolitem action='UnitsAction' />"
219         "    <separator />"
220         "    <toolitem action='transform_affect_label' />"
221         "    <toolitem action='transform_stroke' />"
222         "    <toolitem action='transform_corners' />"
223         "    <toolitem action='transform_gradient' />"
224         "    <toolitem action='transform_pattern' />"
225         "  </toolbar>"
227         "  <toolbar name='NodeToolbar'>"
228         "    <toolitem action='NodeInsertAction' />"
229         "    <toolitem action='NodeDeleteAction' />"
230         "    <separator />"
231         "    <toolitem action='NodeJoinAction' />"
232         "    <toolitem action='NodeJoinSegmentAction' />"
233         "    <toolitem action='NodeDeleteSegmentAction' />"
234         "    <toolitem action='NodeBreakAction' />"
235         "    <separator />"
236         "    <toolitem action='NodeCuspAction' />"
237         "    <toolitem action='NodeSmoothAction' />"
238         "    <toolitem action='NodeSymmetricAction' />"
239         "    <separator />"
240         "    <toolitem action='NodeLineAction' />"
241         "    <toolitem action='NodeCurveAction' />"
242         "    <separator />"
243         "    <toolitem action='ObjectToPath' />"
244         "    <toolitem action='StrokeToPath' />"
245         "    <separator />"
246         "    <toolitem action='NodesShowHandlesAction' />"
247         "    <separator />"
248         "    <toolitem action='EditNextLPEParameterAction' />"
249         "    <separator />"
250         "    <toolitem action='NodeXAction' />"
251         "    <toolitem action='NodeYAction' />"
252         "    <toolitem action='NodeUnitsAction' />"
253         "  </toolbar>"
255         "  <toolbar name='TweakToolbar'>"
256         "    <toolitem action='TweakWidthAction' />"
257         "    <separator />"
258         "    <toolitem action='TweakForceAction' />"
259         "    <toolitem action='TweakPressureAction' />"
260         "    <separator />"
261         "    <toolitem action='TweakModeAction' />"
262         "    <separator />"
263         "    <toolitem action='TweakFidelityAction' />"
264         "    <separator />"
265         "    <toolitem action='TweakChannelsLabel' />"
266         "    <toolitem action='TweakDoH' />"
267         "    <toolitem action='TweakDoS' />"
268         "    <toolitem action='TweakDoL' />"
269         "    <toolitem action='TweakDoO' />"
270         "  </toolbar>"
272         "  <toolbar name='ZoomToolbar'>"
273         "    <toolitem action='ZoomIn' />"
274         "    <toolitem action='ZoomOut' />"
275         "    <separator />"
276         "    <toolitem action='Zoom1:0' />"
277         "    <toolitem action='Zoom1:2' />"
278         "    <toolitem action='Zoom2:1' />"
279         "    <separator />"
280         "    <toolitem action='ZoomSelection' />"
281         "    <toolitem action='ZoomDrawing' />"
282         "    <toolitem action='ZoomPage' />"
283         "    <toolitem action='ZoomPageWidth' />"
284         "    <separator />"
285         "    <toolitem action='ZoomPrev' />"
286         "    <toolitem action='ZoomNext' />"
287         "  </toolbar>"
289         "  <toolbar name='StarToolbar'>"
290         "    <separator />"
291         "    <toolitem action='StarStateAction' />"
292         "    <separator />"
293         "    <toolitem action='FlatAction' />"
294         "    <separator />"
295         "    <toolitem action='MagnitudeAction' />"
296         "    <toolitem action='SpokeAction' />"
297         "    <toolitem action='RoundednessAction' />"
298         "    <toolitem action='RandomizationAction' />"
299         "    <separator />"
300         "    <toolitem action='StarResetAction' />"
301         "  </toolbar>"
303         "  <toolbar name='RectToolbar'>"
304         "    <toolitem action='RectStateAction' />"
305         "    <toolitem action='RectWidthAction' />"
306         "    <toolitem action='RectHeightAction' />"
307         "    <toolitem action='RadiusXAction' />"
308         "    <toolitem action='RadiusYAction' />"
309         "    <toolitem action='RectUnitsAction' />"
310         "    <separator />"
311         "    <toolitem action='RectResetAction' />"
312         "  </toolbar>"
314         "  <toolbar name='3DBoxToolbar'>"
315         "    <toolitem action='3DBoxPosAngleXAction' />"
316         "    <toolitem action='3DBoxVPXAction' />"
317         "    <separator />"
318         "    <toolitem action='3DBoxPosAngleYAction' />"
319         "    <toolitem action='3DBoxVPYAction' />"
320         "    <separator />"
321         "    <toolitem action='3DBoxPosAngleZAction' />"
322         "    <toolitem action='3DBoxVPZAction' />"
323         "    <separator />"
324         "  </toolbar>"
326         "  <toolbar name='SpiralToolbar'>"
327         "    <toolitem action='SpiralStateAction' />"
328         "    <toolitem action='SpiralRevolutionAction' />"
329         "    <toolitem action='SpiralExpansionAction' />"
330         "    <toolitem action='SpiralT0Action' />"
331         "    <separator />"
332         "    <toolitem action='SpiralResetAction' />"
333         "  </toolbar>"
335         "  <toolbar name='PenToolbar'>"
336         "  </toolbar>"
338         "  <toolbar name='PencilToolbar'>"
339         "  </toolbar>"
341         "  <toolbar name='CalligraphyToolbar'>"
342         "    <separator />"
343         "    <toolitem action='CalligraphyWidthAction' />"
344         "    <toolitem action='PressureAction' />"
345         "    <toolitem action='TraceAction' />"
346         "    <toolitem action='ThinningAction' />"
347         "    <separator />"
348         "    <toolitem action='AngleAction' />"
349         "    <toolitem action='TiltAction' />"
350         "    <toolitem action='FixationAction' />"
351         "    <separator />"
352         "    <toolitem action='CapRoundingAction' />"
353         "    <separator />"
354         "    <toolitem action='TremorAction' />"
355         "    <toolitem action='WiggleAction' />"
356         "    <toolitem action='MassAction' />"
357         "    <separator />"
358         "    <toolitem action='CalligraphyResetAction' />"
359         "  </toolbar>"
361         "  <toolbar name='ArcToolbar'>"
362         "    <toolitem action='ArcStateAction' />"
363         "    <separator />"
364         "    <toolitem action='ArcStartAction' />"
365         "    <toolitem action='ArcEndAction' />"
366         "    <separator />"
367         "    <toolitem action='ArcOpenAction' />"
368         "    <separator />"
369         "    <toolitem action='ArcResetAction' />"
370         "    <separator />"
371         "  </toolbar>"
373         "  <toolbar name='PaintbucketToolbar'>"
374         "    <toolitem action='ChannelsAction' />"
375         "    <separator />"
376         "    <toolitem action='ThresholdAction' />"
377         "    <separator />"
378         "    <toolitem action='OffsetAction' />"
379         "    <toolitem action='PaintbucketUnitsAction' />"
380         "    <separator />"
381         "    <toolitem action='AutoGapAction' />"
382         "    <separator />"
383         "    <toolitem action='PaintbucketResetAction' />"
384         "  </toolbar>"
386         "  <toolbar name='DropperToolbar'>"
387         "    <toolitem action='DropperPickAlphaAction' />"
388         "    <toolitem action='DropperSetAlphaAction' />"
389         "  </toolbar>"
391         "  <toolbar name='ConnectorToolbar'>"
392         "    <toolitem action='ConnectorAvoidAction' />"
393         "    <toolitem action='ConnectorIgnoreAction' />"
394         "    <toolitem action='ConnectorSpacingAction' />"
395         "    <toolitem action='ConnectorGraphAction' />"
396         "    <toolitem action='ConnectorLengthAction' />"
397         "    <toolitem action='ConnectorDirectedAction' />"
398         "    <toolitem action='ConnectorOverlapAction' />"
399         "  </toolbar>"
401         "</ui>"
404 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
406 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
408 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
409 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
411 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
412 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
414 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
415 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
417 /* Global text entry widgets necessary for update */
418 /* GtkWidget *dropper_rgb_entry,
419           *dropper_opacity_entry ; */
420 // should be made a private member once this is converted to class
422 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
423     connection->disconnect();
424     delete connection;
427 static void purge_repr_listener( GObject* obj, GObject* tbl )
429     (void)obj;
430     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
431     if (oldrepr) { // remove old listener
432         sp_repr_remove_listener_by_data(oldrepr, tbl);
433         Inkscape::GC::release(oldrepr);
434         oldrepr = 0;
435         g_object_set_data( tbl, "repr", NULL );
436     }
439 GtkWidget *
440 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
441                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
442                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
444     SPAction *action = verb->get_action(view);
445     if (!action) return NULL;
447     SPAction *doubleclick_action;
448     if (doubleclick_verb)
449         doubleclick_action = doubleclick_verb->get_action(view);
450     else
451         doubleclick_action = NULL;
453     /* fixme: Handle sensitive/unsensitive */
454     /* fixme: Implement sp_button_new_from_action */
455     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
456     gtk_widget_show(b);
457     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
459     return b;
462 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
463                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
465     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
468 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
469                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
471     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
475 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
477     SPAction* targetAction = SP_ACTION(user_data);
478     if ( targetAction ) {
479         sp_action_perform( targetAction, NULL );
480     }
483 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
485     if ( data ) {
486         GtkAction* act = GTK_ACTION(data);
487         gtk_action_set_sensitive( act, sensitive );
488     }
491 static SPActionEventVector action_event_vector = {
492     {NULL},
493     NULL,
494     NULL,
495     sp_action_action_set_sensitive,
496     NULL,
497     NULL
498 };
500 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
502     GtkAction* act = 0;
504     SPAction* targetAction = verb->get_action(view);
505     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
506     act = GTK_ACTION(inky);
507     gtk_action_set_sensitive( act, targetAction->sensitive );
509     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
511     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
512     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
514     return act;
517 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
519     Inkscape::UI::View::View *view = desktop;
520     gint verbsToUse[] = {
521         // disabled until we have icons for them:
522         //find
523         //SP_VERB_EDIT_TILE,
524         //SP_VERB_EDIT_UNTILE,
525         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
526         SP_VERB_DIALOG_DISPLAY,
527         SP_VERB_DIALOG_FILL_STROKE,
528         SP_VERB_DIALOG_NAMEDVIEW,
529         SP_VERB_DIALOG_TEXT,
530         SP_VERB_DIALOG_XML_EDITOR,
531         SP_VERB_EDIT_CLONE,
532         SP_VERB_EDIT_COPY,
533         SP_VERB_EDIT_CUT,
534         SP_VERB_EDIT_DUPLICATE,
535         SP_VERB_EDIT_PASTE,
536         SP_VERB_EDIT_REDO,
537         SP_VERB_EDIT_UNDO,
538         SP_VERB_EDIT_UNLINK_CLONE,
539         SP_VERB_FILE_EXPORT,
540         SP_VERB_FILE_IMPORT,
541         SP_VERB_FILE_NEW,
542         SP_VERB_FILE_OPEN,
543         SP_VERB_FILE_PRINT,
544         SP_VERB_FILE_SAVE,
545         SP_VERB_OBJECT_TO_CURVE,
546         SP_VERB_SELECTION_GROUP,
547         SP_VERB_SELECTION_OUTLINE,
548         SP_VERB_SELECTION_UNGROUP,
549         SP_VERB_ZOOM_1_1,
550         SP_VERB_ZOOM_1_2,
551         SP_VERB_ZOOM_2_1,
552         SP_VERB_ZOOM_DRAWING,
553         SP_VERB_ZOOM_IN,
554         SP_VERB_ZOOM_NEXT,
555         SP_VERB_ZOOM_OUT,
556         SP_VERB_ZOOM_PAGE,
557         SP_VERB_ZOOM_PAGE_WIDTH,
558         SP_VERB_ZOOM_PREV,
559         SP_VERB_ZOOM_SELECTION,
560     };
562     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
563     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
565     static std::map<SPDesktop*, GtkActionGroup*> groups;
566     GtkActionGroup* mainActions = 0;
567     if ( groups.find(desktop) != groups.end() ) {
568         mainActions = groups[desktop];
569     }
571     if ( !mainActions ) {
572         mainActions = gtk_action_group_new("main");
573         groups[desktop] = mainActions;
574     }
576     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
577         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
578         if ( verb ) {
579             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
580                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
581                 gtk_action_group_add_action( mainActions, act );
582             }
583         }
584     }
586     return mainActions;
590 GtkWidget *
591 sp_tool_toolbox_new()
593     GtkTooltips *tt = gtk_tooltips_new();
594     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
596     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
597     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
599     gtk_widget_set_sensitive(tb, FALSE);
601     GtkWidget *hb = gtk_handle_box_new();
602     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
603     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
604     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
606     gtk_container_add(GTK_CONTAINER(hb), tb);
607     gtk_widget_show(GTK_WIDGET(tb));
609     sigc::connection* conn = new sigc::connection;
610     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
612     return hb;
615 static void
616 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
618     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
619     gtk_widget_queue_resize(child);
622 static void
623 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
625     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
626     gtk_widget_queue_resize(child);
629 GtkWidget *
630 sp_aux_toolbox_new()
632     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
634     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
635     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
636     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
637     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
638     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
640     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
641     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
643     gtk_widget_set_sensitive(tb, FALSE);
645     GtkWidget *hb = gtk_handle_box_new();
646     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
647     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
648     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
650     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
651     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
653     gtk_container_add(GTK_CONTAINER(hb), tb);
654     gtk_widget_show(GTK_WIDGET(tb));
656     sigc::connection* conn = new sigc::connection;
657     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
659     return hb;
662 //####################################
663 //# Commands Bar
664 //####################################
666 GtkWidget *
667 sp_commands_toolbox_new()
669     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
671     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
672     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
673     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
674     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
675     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
677     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
678     gtk_widget_set_sensitive(tb, FALSE);
680     GtkWidget *hb = gtk_handle_box_new();
681     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
682     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
683     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
685     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
686     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
688     gtk_container_add(GTK_CONTAINER(hb), tb);
689     gtk_widget_show(GTK_WIDGET(tb));
691     sigc::connection* conn = new sigc::connection;
692     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
694     return hb;
697 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
698                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
699                                                        gchar const *path, gchar const *data, gdouble def,
700                                                        GtkWidget *focusTarget,
701                                                        GtkWidget *us,
702                                                        GObject *dataKludge,
703                                                        gboolean altx, gchar const *altx_mark,
704                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
705                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
706                                                        void (*callback)(GtkAdjustment *, GObject *),
707                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
709     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
710                                                              lower, upper, step, page, page ) );
711     if (us) {
712         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
713     }
715     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
717     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
718     if ( shortLabel ) {
719         g_object_set( act, "short_label", shortLabel, NULL );
720     }
722     if ( (descrCount > 0) && descrLabels && descrValues ) {
723         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
724     }
726     if ( focusTarget ) {
727         ege_adjustment_action_set_focuswidget( act, focusTarget );
728     }
730     if ( altx && altx_mark ) {
731         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
732     }
734     if ( dataKludge ) {
735         g_object_set_data( dataKludge, data, adj );
736     }
738     // Using a cast just to make sure we pass in the right kind of function pointer
739     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
741     return act;
745 //####################################
746 //# node editing callbacks
747 //####################################
749 /**
750  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
751  */
752 static ShapeEditor *get_current_shape_editor()
754     if (!SP_ACTIVE_DESKTOP) {
755         return NULL;
756     }
758     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
760     if (!SP_IS_NODE_CONTEXT(event_context)) {
761         return NULL;
762     }
764     return SP_NODE_CONTEXT(event_context)->shape_editor;
768 void
769 sp_node_path_edit_add(void)
771     ShapeEditor *shape_editor = get_current_shape_editor();
772     if (shape_editor) shape_editor->add_node();
775 void
776 sp_node_path_edit_delete(void)
778     ShapeEditor *shape_editor = get_current_shape_editor();
779     if (shape_editor) shape_editor->delete_nodes();
782 void
783 sp_node_path_edit_delete_segment(void)
785     ShapeEditor *shape_editor = get_current_shape_editor();
786     if (shape_editor) shape_editor->delete_segment();
789 void
790 sp_node_path_edit_break(void)
792     ShapeEditor *shape_editor = get_current_shape_editor();
793     if (shape_editor) shape_editor->break_at_nodes();
796 void
797 sp_node_path_edit_join(void)
799     ShapeEditor *shape_editor = get_current_shape_editor();
800     if (shape_editor) shape_editor->join_nodes();
803 void
804 sp_node_path_edit_join_segment(void)
806     ShapeEditor *shape_editor = get_current_shape_editor();
807     if (shape_editor) shape_editor->join_segments();
810 void
811 sp_node_path_edit_toline(void)
813     ShapeEditor *shape_editor = get_current_shape_editor();
814     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
817 void
818 sp_node_path_edit_tocurve(void)
820     ShapeEditor *shape_editor = get_current_shape_editor();
821     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
824 void
825 sp_node_path_edit_cusp(void)
827     ShapeEditor *shape_editor = get_current_shape_editor();
828     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
831 void
832 sp_node_path_edit_smooth(void)
834     ShapeEditor *shape_editor = get_current_shape_editor();
835     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
838 void
839 sp_node_path_edit_symmetrical(void)
841     ShapeEditor *shape_editor = get_current_shape_editor();
842     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
845 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
846     bool show = gtk_toggle_action_get_active( act );
847     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
848     ShapeEditor *shape_editor = get_current_shape_editor();
849     if (shape_editor) shape_editor->show_handles(show);
852 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
853     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
856 /* is called when the node selection is modified */
857 static void
858 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
860     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
861     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
862     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
863     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
865     // quit if run by the attr_changed listener
866     if (g_object_get_data( tbl, "freeze" )) {
867         return;
868     }
870     // in turn, prevent listener from responding
871     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
873     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
874     SPUnit const *unit = tracker->getActiveUnit();
876     ShapeEditor *shape_editor = get_current_shape_editor();
877     if (shape_editor && shape_editor->has_nodepath()) {
878         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
879         int n_selected = 0;
880         if (nodepath) {
881             n_selected = nodepath->numSelected();
882         }
884         if (n_selected == 0) {
885             gtk_action_set_sensitive(xact, FALSE);
886             gtk_action_set_sensitive(yact, FALSE);
887         } else {
888             gtk_action_set_sensitive(xact, TRUE);
889             gtk_action_set_sensitive(yact, TRUE);
890             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
891             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
893             if (n_selected == 1) {
894                 NR::Point sel_node = nodepath->singleSelectedCoords();
895                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
896                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
897                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
898                 }
899             } else {
900                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
901                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
902                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
903                     /* Note: Currently x and y will always have a value, even if the coordinates of the
904                        selected nodes don't coincide (in this case we use the coordinates of the center
905                        of the bounding box). So the entries are never set to zero. */
906                     // FIXME: Maybe we should clear the entry if several nodes are selected
907                     //        instead of providing a kind of average value
908                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
909                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
910                 }
911             }
912         }
913     } else {
914         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
915         gtk_action_set_sensitive(xact, FALSE);
916         gtk_action_set_sensitive(yact, FALSE);
917     }
919     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
922 static void
923 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
925     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
927     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
928     SPUnit const *unit = tracker->getActiveUnit();
930     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
931         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
932     }
934     // quit if run by the attr_changed listener
935     if (g_object_get_data( tbl, "freeze" )) {
936         return;
937     }
939     // in turn, prevent listener from responding
940     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
942     ShapeEditor *shape_editor = get_current_shape_editor();
943     if (shape_editor && shape_editor->has_nodepath()) {
944         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
945         if (!strcmp(value_name, "x")) {
946             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
947         }
948         if (!strcmp(value_name, "y")) {
949             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
950         }
951     }
953     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
956 static void
957 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
959     sp_node_path_value_changed(adj, tbl, "x");
962 static void
963 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
965     sp_node_path_value_changed(adj, tbl, "y");
968 //################################
969 //##    Node Editing Toolbox    ##
970 //################################
972 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
974     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
975     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
976     g_object_set_data( holder, "tracker", tracker );
978     {
979         InkAction* inky = ink_action_new( "NodeInsertAction",
980                                           _("Insert node"),
981                                           _("Insert new nodes into selected segments"),
982                                           "node_insert",
983                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
984         g_object_set( inky, "short_label", _("Insert"), NULL );
985         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
986         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
987     }
989     {
990         InkAction* inky = ink_action_new( "NodeDeleteAction",
991                                           _("Delete node"),
992                                           _("Delete selected nodes"),
993                                           "node_delete",
994                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
995         g_object_set( inky, "short_label", _("Delete"), NULL );
996         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
997         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
998     }
1000     {
1001         InkAction* inky = ink_action_new( "NodeJoinAction",
1002                                           _("Join endnodes"),
1003                                           _("Join selected endnodes"),
1004                                           "node_join",
1005                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1006         g_object_set( inky, "short_label", _("Join"), NULL );
1007         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1008         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1009     }
1011     {
1012         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1013                                           _("Join Segment"),
1014                                           _("Join selected endnodes with a new segment"),
1015                                           "node_join_segment",
1016                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1017         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1018         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1019     }
1021     {
1022         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1023                                           _("Delete Segment"),
1024                                           _("Split path between two non-endpoint nodes"),
1025                                           "node_delete_segment",
1026                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1027         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1028         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1029     }
1031     {
1032         InkAction* inky = ink_action_new( "NodeBreakAction",
1033                                           _("Node Break"),
1034                                           _("Break path at selected nodes"),
1035                                           "node_break",
1036                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1037         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1038         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1039     }
1041     {
1042         InkAction* inky = ink_action_new( "NodeCuspAction",
1043                                           _("Node Cusp"),
1044                                           _("Make selected nodes corner"),
1045                                           "node_cusp",
1046                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1047         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1048         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1049     }
1051     {
1052         InkAction* inky = ink_action_new( "NodeSmoothAction",
1053                                           _("Node Smooth"),
1054                                           _("Make selected nodes smooth"),
1055                                           "node_smooth",
1056                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1057         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1058         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1059     }
1061     {
1062         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1063                                           _("Node Symmetric"),
1064                                           _("Make selected nodes symmetric"),
1065                                           "node_symmetric",
1066                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1067         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1068         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1069     }
1071     {
1072         InkAction* inky = ink_action_new( "NodeLineAction",
1073                                           _("Node Line"),
1074                                           _("Make selected segments lines"),
1075                                           "node_line",
1076                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1077         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1078         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1079     }
1081     {
1082         InkAction* inky = ink_action_new( "NodeCurveAction",
1083                                           _("Node Curve"),
1084                                           _("Make selected segments curves"),
1085                                           "node_curve",
1086                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1087         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1088         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1089     }
1091     {
1092         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1093                                                       _("Show Handles"),
1094                                                       _("Show the Bezier handles of selected nodes"),
1095                                                       "nodes_show_handles",
1096                                                       Inkscape::ICON_SIZE_DECORATION );
1097         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1098         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1099         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1100     }
1102     {
1103         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1104                                           _("Next Path Effect Parameter"),
1105                                           _("Show next Path Effect parameter for editing"),
1106                                           "edit_next_parameter",
1107                                           Inkscape::ICON_SIZE_DECORATION );
1108         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1109         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1110     }
1112     /* X coord of selected node(s) */
1113     {
1114         EgeAdjustmentAction* eact = 0;
1115         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1116         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1117         eact = create_adjustment_action( "NodeXAction",
1118                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1119                                          "tools.nodes", "Xcoord", 0,
1120                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1121                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1122                                          labels, values, G_N_ELEMENTS(labels),
1123                                          sp_node_path_x_value_changed );
1124         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1125         g_object_set_data( holder, "nodes_x_action", eact );
1126         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1127         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1128     }
1130     /* Y coord of selected node(s) */
1131     {
1132         EgeAdjustmentAction* eact = 0;
1133         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1134         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1135         eact = create_adjustment_action( "NodeYAction",
1136                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1137                                          "tools.nodes", "Ycoord", 0,
1138                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1139                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1140                                          labels, values, G_N_ELEMENTS(labels),
1141                                          sp_node_path_y_value_changed );
1142         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1143         g_object_set_data( holder, "nodes_y_action", eact );
1144         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1145         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1146     }
1148     // add the units menu
1149     {
1150         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1151         gtk_action_group_add_action( mainActions, act );
1152     }
1154     sigc::connection *connection = new sigc::connection (
1155         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1156         );
1158     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1159     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1160 } // end of sp_node_toolbox_prep()
1163 //########################
1164 //##    Zoom Toolbox    ##
1165 //########################
1167 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1169     // no custom GtkAction setup needed
1170 } // end of sp_zoom_toolbox_prep()
1172 void
1173 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1175     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")));
1179 void
1180 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1182     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")));
1185 void
1186 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1188     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")));
1191 static void
1192 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1194     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1195     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1197     if (old_desktop) {
1198         GList *children, *iter;
1200         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1201         for ( iter = children ; iter ; iter = iter->next ) {
1202             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1203         }
1204         g_list_free(children);
1205     }
1207     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1209     if (desktop) {
1210         gtk_widget_set_sensitive(toolbox, TRUE);
1211         setup_func(toolbox, desktop);
1212         update_func(desktop, desktop->event_context, toolbox);
1213         *conn = desktop->connectEventContextChanged
1214             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1215     } else {
1216         gtk_widget_set_sensitive(toolbox, FALSE);
1217     }
1219 } // end of toolbox_set_desktop()
1222 static void
1223 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1225     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1226     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1227     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1228         // "toolbox.tools" was not set. Fallback to older value
1229         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1231         // Copy the setting forwards
1232         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1233     }
1234     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1236     for (int i = 0 ; tools[i].type_name ; i++ ) {
1237         GtkWidget *button =
1238             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1239                                                               SP_BUTTON_TYPE_TOGGLE,
1240                                                               Inkscape::Verb::get(tools[i].verb),
1241                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1242                                                               desktop,
1243                                                               tooltips );
1245         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1246                            (gpointer)button );
1247     }
1251 static void
1252 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1254     gchar const *const tname = ( eventcontext
1255                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1256                                  : NULL );
1257     for (int i = 0 ; tools[i].type_name ; i++ ) {
1258         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1259         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1260     }
1263 static void
1264 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1266     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1267     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1268     GtkUIManager* mgr = gtk_ui_manager_new();
1269     GError* errVal = 0;
1270     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1271     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1273     std::map<std::string, GtkWidget*> dataHolders;
1275     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1276         if ( aux_toolboxes[i].prep_func ) {
1277             // converted to GtkActions and UIManager
1279             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1280             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1281             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1282             dataHolders[aux_toolboxes[i].type_name] = kludge;
1283             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1284         } else {
1286             GtkWidget *sub_toolbox = 0;
1287             if (aux_toolboxes[i].create_func == NULL)
1288                 sub_toolbox = sp_empty_toolbox_new(desktop);
1289             else {
1290                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1291             }
1293             gtk_size_group_add_widget( grouper, sub_toolbox );
1295             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1296             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1298         }
1299     }
1301     // Second pass to create toolbars *after* all GtkActions are created
1302     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1303         if ( aux_toolboxes[i].prep_func ) {
1304             // converted to GtkActions and UIManager
1306             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1308             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1309             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1311             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1312             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1313             g_free( tmp );
1314             tmp = 0;
1316             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1317             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1318             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1319                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1320             }
1321             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1324             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1326             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1327                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1328                 swatch->setDesktop( desktop );
1329                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1330                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1331                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1332                 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 );
1333             }
1335             gtk_widget_show_all( holder );
1336             sp_set_font_size_smaller( holder );
1338             gtk_size_group_add_widget( grouper, holder );
1340             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1341             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1342         }
1343     }
1345     g_object_unref( G_OBJECT(grouper) );
1348 static void
1349 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1351     gchar const *tname = ( eventcontext
1352                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1353                            : NULL );
1354     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1355         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1356         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1357             gtk_widget_show_all(sub_toolbox);
1358             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1359         } else {
1360             gtk_widget_hide(sub_toolbox);
1361         }
1362     }
1365 static void
1366 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1368     gchar const * descr =
1369         "<ui>"
1370         "  <toolbar name='CommandsToolbar'>"
1371         "    <toolitem action='FileNew' />"
1372         "    <toolitem action='FileOpen' />"
1373         "    <toolitem action='FileSave' />"
1374         "    <toolitem action='FilePrint' />"
1375         "    <separator />"
1376         "    <toolitem action='FileImport' />"
1377         "    <toolitem action='FileExport' />"
1378         "    <separator />"
1379         "    <toolitem action='EditUndo' />"
1380         "    <toolitem action='EditRedo' />"
1381         "    <separator />"
1382         "    <toolitem action='EditCopy' />"
1383         "    <toolitem action='EditCut' />"
1384         "    <toolitem action='EditPaste' />"
1385         "    <separator />"
1386         "    <toolitem action='ZoomSelection' />"
1387         "    <toolitem action='ZoomDrawing' />"
1388         "    <toolitem action='ZoomPage' />"
1389         "    <separator />"
1390         "    <toolitem action='EditDuplicate' />"
1391         "    <toolitem action='EditClone' />"
1392         "    <toolitem action='EditUnlinkClone' />"
1393         "    <separator />"
1394         "    <toolitem action='SelectionGroup' />"
1395         "    <toolitem action='SelectionUnGroup' />"
1396         "    <separator />"
1397         "    <toolitem action='DialogFillStroke' />"
1398         "    <toolitem action='DialogText' />"
1399         "    <toolitem action='DialogXMLEditor' />"
1400         "    <toolitem action='DialogAlignDistribute' />"
1401         "    <separator />"
1402         "    <toolitem action='DialogPreferences' />"
1403         "    <toolitem action='DialogDocumentProperties' />"
1404         "  </toolbar>"
1405         "</ui>";
1406     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1409     GtkUIManager* mgr = gtk_ui_manager_new();
1410     GError* errVal = 0;
1412     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1413     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1415     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1416     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1417         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1418     }
1419     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1420     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1421     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1424     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1427 static void
1428 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1432 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1434     gtk_widget_show(toolbox_toplevel);
1435     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1437     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1438     if (!shown_toolbox) {
1439         return;
1440     }
1441     gtk_widget_show(toolbox);
1443     // need to show the spacer, or the padding will be off
1444     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1445     gtk_widget_show(spacer);
1447     gtk_widget_show_all(shown_toolbox);
1450 void
1451 aux_toolbox_space(GtkWidget *tb, gint space)
1453     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1456 static GtkWidget *
1457 sp_empty_toolbox_new(SPDesktop *desktop)
1459     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1460     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1461     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1463     gtk_widget_show_all(tbl);
1464     sp_set_font_size_smaller (tbl);
1466     return tbl;
1469 // helper UI functions
1471 GtkWidget *
1472 sp_tb_spinbutton(
1473     gchar *label, gchar const *tooltip,
1474     gchar const *path, gchar const *data, gdouble def,
1475     GtkWidget *us,
1476     GtkWidget *tbl,
1477     gboolean altx, gchar const *altx_mark,
1478     gdouble lower, gdouble upper, gdouble step, gdouble page,
1479     void (*callback)(GtkAdjustment *, GtkWidget *),
1480     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1482     GtkTooltips *tt = gtk_tooltips_new();
1484     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1486     GtkWidget *l = gtk_label_new(label);
1487     gtk_widget_show(l);
1488     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1489     gtk_container_add(GTK_CONTAINER(hb), l);
1491     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1492                                       lower, upper, step, page, page);
1493     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1494     if (us)
1495         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1497     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1498     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1499     if (altx)
1500         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1501     gtk_widget_set_size_request(sb,
1502                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1503                                 AUX_SPINBUTTON_HEIGHT);
1504     gtk_widget_show(sb);
1505     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1506     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1507     gtk_container_add(GTK_CONTAINER(hb), sb);
1508     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1510     return hb;
1513 #define MODE_LABEL_WIDTH 70
1515 //########################
1516 //##       Star         ##
1517 //########################
1519 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1521     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1523     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1524         // do not remember prefs if this call is initiated by an undo change, because undoing object
1525         // creation sets bogus values to its attributes before it is deleted
1526         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1527     }
1529     // quit if run by the attr_changed listener
1530     if (g_object_get_data( dataKludge, "freeze" )) {
1531         return;
1532     }
1534     // in turn, prevent listener from responding
1535     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1537     bool modmade = false;
1539     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1540     GSList const *items = selection->itemList();
1541     for (; items != NULL; items = items->next) {
1542         if (SP_IS_STAR((SPItem *) items->data)) {
1543             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1544             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1545             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1546                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1547                                     + M_PI / (gint)adj->value));
1548             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1549             modmade = true;
1550         }
1551     }
1552     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1553                                    _("Star: Change number of corners"));
1555     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1558 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1560     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1562     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1563         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1564     }
1566     // quit if run by the attr_changed listener
1567     if (g_object_get_data( dataKludge, "freeze" )) {
1568         return;
1569     }
1571     // in turn, prevent listener from responding
1572     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1574     bool modmade = false;
1575     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1576     GSList const *items = selection->itemList();
1577     for (; items != NULL; items = items->next) {
1578         if (SP_IS_STAR((SPItem *) items->data)) {
1579             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1581             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1582             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1583             if (r2 < r1) {
1584                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1585             } else {
1586                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1587             }
1589             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1590             modmade = true;
1591         }
1592     }
1594     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1595                                    _("Star: Change spoke ratio"));
1597     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1600 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1602     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1603     bool flat = ege_select_one_action_get_active( act ) == 0;
1605     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1606         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1607                                     flat ? "true" : "false" );
1608     }
1610     // quit if run by the attr_changed listener
1611     if (g_object_get_data( dataKludge, "freeze" )) {
1612         return;
1613     }
1615     // in turn, prevent listener from responding
1616     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1618     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1619     GSList const *items = selection->itemList();
1620     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1621     bool modmade = false;
1623     if ( prop_action ) {
1624         gtk_action_set_sensitive( prop_action, !flat );
1625     }
1627     for (; items != NULL; items = items->next) {
1628         if (SP_IS_STAR((SPItem *) items->data)) {
1629             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1630             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1631             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1632             modmade = true;
1633         }
1634     }
1636     if (modmade) {
1637         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1638                          flat ? _("Make polygon") : _("Make star"));
1639     }
1641     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1644 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1646     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1648     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1649         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1650     }
1652     // quit if run by the attr_changed listener
1653     if (g_object_get_data( dataKludge, "freeze" )) {
1654         return;
1655     }
1657     // in turn, prevent listener from responding
1658     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1660     bool modmade = false;
1662     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1663     GSList const *items = selection->itemList();
1664     for (; items != NULL; items = items->next) {
1665         if (SP_IS_STAR((SPItem *) items->data)) {
1666             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1667             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1668             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1669             modmade = true;
1670         }
1671     }
1672     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1673                                    _("Star: Change rounding"));
1675     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1678 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1680     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1682     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1683         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1684     }
1686     // quit if run by the attr_changed listener
1687     if (g_object_get_data( dataKludge, "freeze" )) {
1688         return;
1689     }
1691     // in turn, prevent listener from responding
1692     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1694     bool modmade = false;
1696     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1697     GSList const *items = selection->itemList();
1698     for (; items != NULL; items = items->next) {
1699         if (SP_IS_STAR((SPItem *) items->data)) {
1700             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1701             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1702             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1703             modmade = true;
1704         }
1705     }
1706     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1707                                    _("Star: Change randomization"));
1709     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1713 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1714                                        gchar const */*old_value*/, gchar const */*new_value*/,
1715                                        bool /*is_interactive*/, gpointer data)
1717     GtkWidget *tbl = GTK_WIDGET(data);
1719     // quit if run by the _changed callbacks
1720     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1721         return;
1722     }
1724     // in turn, prevent callbacks from responding
1725     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1727     GtkAdjustment *adj = 0;
1729     if (!strcmp(name, "inkscape:randomized")) {
1730         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1731         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1732     } else if (!strcmp(name, "inkscape:rounded")) {
1733         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1734         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1735     } else if (!strcmp(name, "inkscape:flatsided")) {
1736         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1737         char const *flatsides = repr->attribute("inkscape:flatsided");
1738         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1739         if ( flatsides && !strcmp(flatsides,"false") ) {
1740             ege_select_one_action_set_active( flat_action, 1 );
1741             gtk_action_set_sensitive( prop_action, TRUE );
1742         } else {
1743             ege_select_one_action_set_active( flat_action, 0 );
1744             gtk_action_set_sensitive( prop_action, FALSE );
1745         }
1746     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1747         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1748         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1749         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1750         if (r2 < r1) {
1751             gtk_adjustment_set_value(adj, r2/r1);
1752         } else {
1753             gtk_adjustment_set_value(adj, r1/r2);
1754         }
1755     } else if (!strcmp(name, "sodipodi:sides")) {
1756         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1757         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1758     }
1760     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1764 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1766     NULL, /* child_added */
1767     NULL, /* child_removed */
1768     star_tb_event_attr_changed,
1769     NULL, /* content_changed */
1770     NULL  /* order_changed */
1771 };
1774 /**
1775  *  \param selection Should not be NULL.
1776  */
1777 static void
1778 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1780     int n_selected = 0;
1781     Inkscape::XML::Node *repr = NULL;
1783     purge_repr_listener( tbl, tbl );
1785     for (GSList const *items = selection->itemList();
1786          items != NULL;
1787          items = items->next)
1788     {
1789         if (SP_IS_STAR((SPItem *) items->data)) {
1790             n_selected++;
1791             repr = SP_OBJECT_REPR((SPItem *) items->data);
1792         }
1793     }
1795     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1797     if (n_selected == 0) {
1798         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1799     } else if (n_selected == 1) {
1800         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1802         if (repr) {
1803             g_object_set_data( tbl, "repr", repr );
1804             Inkscape::GC::anchor(repr);
1805             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1806             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1807         }
1808     } else {
1809         // FIXME: implement averaging of all parameters for multiple selected stars
1810         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1811         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1812     }
1816 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1818     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1819     // callbacks to lump all the changes for all selected objects in one undo step
1821     GtkAdjustment *adj = 0;
1823     // fixme: make settable in prefs!
1824     gint mag = 5;
1825     gdouble prop = 0.5;
1826     gboolean flat = FALSE;
1827     gdouble randomized = 0;
1828     gdouble rounded = 0;
1830     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1831     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1833     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1834     gtk_action_set_sensitive( sb2, !flat );
1836     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1837     gtk_adjustment_set_value(adj, mag);
1838     gtk_adjustment_value_changed(adj);
1840     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1841     gtk_adjustment_set_value(adj, prop);
1842     gtk_adjustment_value_changed(adj);
1844     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1845     gtk_adjustment_set_value(adj, rounded);
1846     gtk_adjustment_value_changed(adj);
1848     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1849     gtk_adjustment_set_value(adj, randomized);
1850     gtk_adjustment_value_changed(adj);
1854 void
1855 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1857     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1858     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1859     GtkWidget *l = gtk_label_new(NULL);
1860     gtk_label_set_markup(GTK_LABEL(l), title);
1861     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1862     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1863     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1867 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1869     {
1870         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1871         ege_output_action_set_use_markup( act, TRUE );
1872         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1873         g_object_set_data( holder, "mode_action", act );
1874     }
1876     {
1877         EgeAdjustmentAction* eact = 0;
1878         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1879         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1881         /* Flatsided checkbox */
1882         {
1883             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1885             GtkTreeIter iter;
1886             gtk_list_store_append( model, &iter );
1887             gtk_list_store_set( model, &iter,
1888                                 0, _("Polygon"),
1889                                 1, _("Regular polygon (with one handle) instead of a star"),
1890                                 2, "star_flat",
1891                                 -1 );
1893             gtk_list_store_append( model, &iter );
1894             gtk_list_store_set( model, &iter,
1895                                 0, _("Star"),
1896                                 1, _("Star instead of a regular polygon (with one handle)"),
1897                                 2, "star_angled",
1898                                 -1 );
1900             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1901             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1902             g_object_set_data( holder, "flat_action", act );
1904             ege_select_one_action_set_appearance( act, "full" );
1905             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1906             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1907             ege_select_one_action_set_icon_column( act, 2 );
1908             ege_select_one_action_set_tooltip_column( act, 1  );
1910             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1911             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1912         }
1914         /* Magnitude */
1915         {
1916         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1917         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1918         eact = create_adjustment_action( "MagnitudeAction",
1919                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1920                                          "tools.shapes.star", "magnitude", 3,
1921                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1922                                          3, 1024, 1, 5,
1923                                          labels, values, G_N_ELEMENTS(labels),
1924                                          sp_stb_magnitude_value_changed,
1925                                          1.0, 0 );
1926         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1927         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1928         }
1930         /* Spoke ratio */
1931         {
1932         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1933         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1934         eact = create_adjustment_action( "SpokeAction",
1935                                          _("Spoke ratio"), _("Spoke ratio:"),
1936                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1937                                          // Base radius is the same for the closest handle.
1938                                          _("Base radius to tip radius ratio"),
1939                                          "tools.shapes.star", "proportion", 0.5,
1940                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1941                                          0.01, 1.0, 0.01, 0.1,
1942                                          labels, values, G_N_ELEMENTS(labels),
1943                                          sp_stb_proportion_value_changed );
1944         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1945         g_object_set_data( holder, "prop_action", eact );
1946         }
1948         if ( !isFlatSided ) {
1949             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1950         } else {
1951             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1952         }
1954         /* Roundedness */
1955         {
1956         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1957         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1958         eact = create_adjustment_action( "RoundednessAction",
1959                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1960                                          "tools.shapes.star", "rounded", 0.0,
1961                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1962                                          -10.0, 10.0, 0.01, 0.1,
1963                                          labels, values, G_N_ELEMENTS(labels),
1964                                          sp_stb_rounded_value_changed );
1965         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1966         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1967         }
1969         /* Randomization */
1970         {
1971         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1972         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1973         eact = create_adjustment_action( "RandomizationAction",
1974                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1975                                          "tools.shapes.star", "randomized", 0.0,
1976                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1977                                          -10.0, 10.0, 0.001, 0.01,
1978                                          labels, values, G_N_ELEMENTS(labels),
1979                                          sp_stb_randomized_value_changed, 0.1, 3 );
1980         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1981         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1982         }
1983     }
1985     {
1986         /* Reset */
1987         {
1988             GtkAction* act = gtk_action_new( "StarResetAction",
1989                                              _("Defaults"),
1990                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1991                                              GTK_STOCK_CLEAR );
1992             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1993             gtk_action_group_add_action( mainActions, act );
1994             gtk_action_set_sensitive( act, TRUE );
1995         }
1996     }
1998     sigc::connection *connection = new sigc::connection(
1999         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2000         );
2001     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2002     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2006 //########################
2007 //##       Rect         ##
2008 //########################
2010 static void sp_rtb_sensitivize( GObject *tbl )
2012     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2013     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2014     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2016     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2017         gtk_action_set_sensitive( not_rounded, FALSE );
2018     } else {
2019         gtk_action_set_sensitive( not_rounded, TRUE );
2020     }
2024 static void
2025 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2026                           void (*setter)(SPRect *, gdouble))
2028     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2030     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2031     SPUnit const *unit = tracker->getActiveUnit();
2033     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2034         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2035     }
2037     // quit if run by the attr_changed listener
2038     if (g_object_get_data( tbl, "freeze" )) {
2039         return;
2040     }
2042     // in turn, prevent listener from responding
2043     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2045     bool modmade = false;
2046     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2047     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2048         if (SP_IS_RECT(items->data)) {
2049             if (adj->value != 0) {
2050                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2051             } else {
2052                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2053             }
2054             modmade = true;
2055         }
2056     }
2058     sp_rtb_sensitivize( tbl );
2060     if (modmade) {
2061         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2062                                    _("Change rectangle"));
2063     }
2065     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2068 static void
2069 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2071     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2074 static void
2075 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2077     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2080 static void
2081 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2083     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2086 static void
2087 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2089     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2094 static void
2095 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2097     GtkAdjustment *adj = 0;
2099     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2100     gtk_adjustment_set_value(adj, 0.0);
2101     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2102     gtk_adjustment_value_changed(adj);
2104     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2105     gtk_adjustment_set_value(adj, 0.0);
2106     gtk_adjustment_value_changed(adj);
2108     sp_rtb_sensitivize( obj );
2111 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2112                                        gchar const */*old_value*/, gchar const */*new_value*/,
2113                                        bool /*is_interactive*/, gpointer data)
2115     GObject *tbl = G_OBJECT(data);
2117     // quit if run by the _changed callbacks
2118     if (g_object_get_data( tbl, "freeze" )) {
2119         return;
2120     }
2122     // in turn, prevent callbacks from responding
2123     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2125     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2126     SPUnit const *unit = tracker->getActiveUnit();
2128     gpointer item = g_object_get_data( tbl, "item" );
2129     if (item && SP_IS_RECT(item)) {
2130         {
2131             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2132             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2133             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2134         }
2136         {
2137             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2138             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2139             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2140         }
2142         {
2143             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2144             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2145             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2146         }
2148         {
2149             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2150             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2151             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2152         }
2153     }
2155     sp_rtb_sensitivize( tbl );
2157     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2161 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2162     NULL, /* child_added */
2163     NULL, /* child_removed */
2164     rect_tb_event_attr_changed,
2165     NULL, /* content_changed */
2166     NULL  /* order_changed */
2167 };
2169 /**
2170  *  \param selection should not be NULL.
2171  */
2172 static void
2173 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2175     int n_selected = 0;
2176     Inkscape::XML::Node *repr = NULL;
2177     SPItem *item = NULL;
2179     if ( g_object_get_data( tbl, "repr" ) ) {
2180         g_object_set_data( tbl, "item", NULL );
2181     }
2182     purge_repr_listener( tbl, tbl );
2184     for (GSList const *items = selection->itemList();
2185          items != NULL;
2186          items = items->next) {
2187         if (SP_IS_RECT((SPItem *) items->data)) {
2188             n_selected++;
2189             item = (SPItem *) items->data;
2190             repr = SP_OBJECT_REPR(item);
2191         }
2192     }
2194     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2196     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2198     if (n_selected == 0) {
2199         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2201         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2202         gtk_action_set_sensitive(w, FALSE);
2203         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2204         gtk_action_set_sensitive(h, FALSE);
2206     } else if (n_selected == 1) {
2207         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2208         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2210         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2211         gtk_action_set_sensitive(w, TRUE);
2212         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2213         gtk_action_set_sensitive(h, TRUE);
2215         if (repr) {
2216             g_object_set_data( tbl, "repr", repr );
2217             g_object_set_data( tbl, "item", item );
2218             Inkscape::GC::anchor(repr);
2219             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2220             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2221         }
2222     } else {
2223         // FIXME: implement averaging of all parameters for multiple selected
2224         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2225         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2226         sp_rtb_sensitivize( tbl );
2227     }
2231 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2233     EgeAdjustmentAction* eact = 0;
2235     {
2236         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2237         ege_output_action_set_use_markup( act, TRUE );
2238         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2239         g_object_set_data( holder, "mode_action", act );
2240     }
2242     // rx/ry units menu: create
2243     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2244     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2245     // fixme: add % meaning per cent of the width/height
2246     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2247     g_object_set_data( holder, "tracker", tracker );
2249     /* W */
2250     {
2251         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2252         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2253         eact = create_adjustment_action( "RectWidthAction",
2254                                          _("Width"), _("W:"), _("Width of rectangle"),
2255                                          "tools.shapes.rect", "width", 0,
2256                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2257                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2258                                          labels, values, G_N_ELEMENTS(labels),
2259                                          sp_rtb_width_value_changed );
2260         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2261         g_object_set_data( holder, "width_action", eact );
2262         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2263         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2264     }
2266     /* H */
2267     {
2268         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2269         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2270         eact = create_adjustment_action( "RectHeightAction",
2271                                          _("Height"), _("H:"), _("Height of rectangle"),
2272                                          "tools.shapes.rect", "height", 0,
2273                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2274                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2275                                          labels, values, G_N_ELEMENTS(labels),
2276                                          sp_rtb_height_value_changed );
2277         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2278         g_object_set_data( holder, "height_action", eact );
2279         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2280         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2281     }
2283     /* rx */
2284     {
2285         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2286         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2287         eact = create_adjustment_action( "RadiusXAction",
2288                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2289                                          "tools.shapes.rect", "rx", 0,
2290                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2291                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2292                                          labels, values, G_N_ELEMENTS(labels),
2293                                          sp_rtb_rx_value_changed);
2294         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2295         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2296     }
2298     /* ry */
2299     {
2300         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2301         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2302         eact = create_adjustment_action( "RadiusYAction",
2303                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2304                                          "tools.shapes.rect", "ry", 0,
2305                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2306                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2307                                          labels, values, G_N_ELEMENTS(labels),
2308                                          sp_rtb_ry_value_changed);
2309         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2310         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2311     }
2313     // add the units menu
2314     {
2315         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2316         gtk_action_group_add_action( mainActions, act );
2317     }
2319     /* Reset */
2320     {
2321         InkAction* inky = ink_action_new( "RectResetAction",
2322                                           _("Not rounded"),
2323                                           _("Make corners sharp"),
2324                                           "squared_corner",
2325                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2326         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2327         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2328         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2329         g_object_set_data( holder, "not_rounded", inky );
2330     }
2332     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2333     sp_rtb_sensitivize( holder );
2335     sigc::connection *connection = new sigc::connection(
2336         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2337         );
2338     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2339     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2342 //########################
2343 //##       3D Box       ##
2344 //########################
2346 static void box3d_toggle_vp_changed (GtkToggleAction *act, GObject *dataKludge, Proj::Axis axis)
2348     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2349     SPDocument *document = sp_desktop_document (desktop);
2350     // FIXME: Make sure document->current_persp3d is set correctly!
2351     Persp3D *persp = document->current_persp3d;
2353     g_return_if_fail (persp);
2355     // quit if run by the attr_changed listener
2356     if (g_object_get_data(dataKludge, "freeze")) {
2357         return;
2358     }
2360     // in turn, prevent listener from responding
2361     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2363     persp3d_set_VP_state(persp, axis, (gtk_toggle_action_get_active(act) ? Proj::VP_INFINITE : Proj::VP_FINITE));
2365     // FIXME: Can we merge this functionality with the one in box3d_persp_tb_event_attr_changed()?
2366     gchar *str;    
2367     switch (axis) {
2368         case Proj::X:
2369             str = g_strdup ("box3d_angle_x_action");
2370             break;
2371         case Proj::Y:
2372             str = g_strdup ("box3d_angle_y_action");
2373             break;
2374         case Proj::Z:
2375             str = g_strdup ("box3d_angle_z_action");
2376             break;
2377         default:
2378             return;
2379     }
2380     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2381     if (angle_action) {
2382         gtk_action_set_sensitive (angle_action, !persp3d_VP_is_finite(persp, axis));
2383     }
2385     sp_document_maybe_done(sp_desktop_document(desktop), "toggle_vp", SP_VERB_CONTEXT_3DBOX,
2386                            _("3D Box: Toggle VP"));
2387     //sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,_("3D Box: Toggle VP"));
2389     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
2392 static void box3d_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2394     box3d_toggle_vp_changed (act, dataKludge, Proj::X);
2397 static void box3d_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2399     box3d_toggle_vp_changed (act, dataKludge, Proj::Y);
2402 static void box3d_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2404     box3d_toggle_vp_changed (act, dataKludge, Proj::Z);
2407 static void box3d_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis )
2409     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2410     Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
2412     // quit if run by the attr_changed listener
2413     if (g_object_get_data(dataKludge, "freeze")) {
2414         return;
2415     }
2417     // in turn, prevent listener from responding
2418     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2420     if (persp) {
2421         double angle = adj->value;
2422         // FIXME: Shouldn't we set the angle via the SVG attributes of the perspective instead of directly?
2423         if (persp3d_VP_is_finite(persp, axis)) {
2424             return; 
2425         }
2426         persp->tmat.set_infinite_direction (axis, angle);
2427         persp3d_update_box_reprs (persp);
2429         sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2430                                _("3D Box: Change perspective"));
2431     }
2433     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
2436 static void box3d_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2438     box3d_vp_angle_changed (adj, dataKludge, Proj::X);
2441 static void box3d_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2443     box3d_vp_angle_changed (adj, dataKludge, Proj::Y);
2446 static void box3d_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2448     box3d_vp_angle_changed (adj, dataKludge, Proj::Z);
2451 // normalize angle so that it lies in the interval [0,360]
2452 static double box3d_normalize_angle (double a) {
2453     double angle = a + ((int) (a/360.0))*360;
2454     if (angle < 0) {
2455         angle += 360.0;
2456     }
2457     return angle;
2460 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2461                                               gchar const *old_value, gchar const *new_value,
2462                                               bool is_interactive, gpointer data)
2464     GtkWidget *tbl = GTK_WIDGET(data);
2466     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2467     // quit if run by the _changed callbacks
2468     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2469         //return;
2470     }
2472     // in turn, prevent callbacks from responding
2473     //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2475     GtkAdjustment *adj = 0;
2476     double angle;
2477     SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2478     // FIXME: Get the persp from the box (should be the same, but ...)
2479     Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
2480     if (!strcmp(name, "inkscape:vp_x")) {
2481         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_x_action"));
2482         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_x_action"));
2483         if (!persp3d_VP_is_finite(persp, Proj::X)) {
2484             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2485             gtk_toggle_action_set_active(tact, TRUE);
2486         } else {
2487             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2488             gtk_toggle_action_set_active(tact, FALSE);
2489         }
2491         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2492         angle = persp3d_get_infinite_angle(persp, Proj::X);
2493         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2494             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2495         }
2496     }
2498     if (!strcmp(name, "inkscape:vp_y")) {
2499         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_y_action"));
2500         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_y_action"));
2501         if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2502             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2503             gtk_toggle_action_set_active(tact, TRUE);
2504         } else {
2505             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2506             gtk_toggle_action_set_active(tact, FALSE);
2507         }
2509         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2510         angle = persp3d_get_infinite_angle(persp, Proj::Y);
2511         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2512             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2513         }
2514     }
2516     if (!strcmp(name, "inkscape:vp_z")) {
2517         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_z_action"));
2518         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_z_action"));
2519         if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2520             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2521             gtk_toggle_action_set_active(tact, TRUE);
2522         } else {
2523             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2524             gtk_toggle_action_set_active(tact, FALSE);
2525         }
2527         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2528         angle = persp3d_get_infinite_angle(persp, Proj::Z);
2529         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2530             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2531         }
2532     }
2533     
2534     //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2537 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2539     NULL, /* child_added */
2540     NULL, /* child_removed */
2541     box3d_persp_tb_event_attr_changed,
2542     NULL, /* content_changed */
2543     NULL  /* order_changed */
2544 };
2546 /**
2547  *  \param selection Should not be NULL.
2548  */
2549 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2550 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2551 static void
2552 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2554     Inkscape::XML::Node *repr = NULL;
2555     purge_repr_listener(tbl, tbl);
2557     SPItem *item = selection->singleItem();
2558     if (item && SP_IS_BOX3D(item)) {
2559         //repr = SP_OBJECT_REPR(item);
2560         repr = SP_OBJECT_REPR(SP_BOX3D(item)->persp_ref->getObject());
2561         if (repr) {
2562             g_object_set_data(tbl, "repr", repr);
2563             Inkscape::GC::anchor(repr);
2564             sp_repr_add_listener(repr, &box3d_persp_tb_repr_events, tbl);
2565             sp_repr_synthesize_events(repr, &box3d_persp_tb_repr_events, tbl);
2566         }
2567     }
2570 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2572     EgeAdjustmentAction* eact = 0;
2573     SPDocument *document = sp_desktop_document (desktop);
2574     Persp3D *persp = document->current_persp3d;
2575     bool toggled = false;
2577     /* angle of VP in X direction */
2578     eact = create_adjustment_action("3DBoxPosAngleXAction",
2579                                     _("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2580                                     "tools.shapes.3dbox", "dir_vp_x", persp3d_get_infinite_angle(persp, Proj::X),
2581                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2582                                     -360.0, 360.0, 1.0, 10.0,
2583                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2584                                     box3d_vpx_angle_changed,
2585                                     0.1, 1);
2586     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2587     g_object_set_data(holder, "box3d_angle_x_action", eact);
2588     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2589         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2590     } else {
2591         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2592     }
2594     /* toggle VP in X direction */
2595     {
2596     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2597                                                   _("Toggle VP in X direction"),
2598                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (= parallel)"),
2599                                                   "toggle_vp_x",
2600                                                   Inkscape::ICON_SIZE_DECORATION);
2601     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2602     g_object_set_data(holder, "toggle_vp_x_action", act);
2603     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::X));
2604     /* we connect the signal after setting the state to avoid switching the state again */
2605     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_x_changed), holder);
2606     }
2608     /* angle of VP in Y direction */
2609     eact = create_adjustment_action("3DBoxPosAngleYAction",
2610                                     _("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2611                                     "tools.shapes.3dbox", "dir_vp_y", persp3d_get_infinite_angle(persp, Proj::Y),
2612                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2613                                     -360.0, 360.0, 1.0, 10.0,
2614                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2615                                     box3d_vpy_angle_changed,
2616                                     0.1, 1);
2617     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2618     g_object_set_data(holder, "box3d_angle_y_action", eact);
2619     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2620         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2621     } else {
2622         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2623     }
2625     /* toggle VP in Y direction */
2626     {
2627     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2628                                                  _("Toggle VP in Y direction"),
2629                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (= parallel)"),
2630                                                  "toggle_vp_y",
2631                                                  Inkscape::ICON_SIZE_DECORATION);
2632     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2633     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Y));
2634     g_object_set_data(holder, "toggle_vp_y_action", act);
2635     /* we connect the signal after setting the state to avoid switching the state again */
2636     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_y_changed), holder);
2637     }
2639     /* angle of VP in Z direction */
2640     eact = create_adjustment_action("3DBoxPosAngleZAction",
2641                                     _("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2642                                     "tools.shapes.3dbox", "dir_vp_z", persp3d_get_infinite_angle(persp, Proj::Z),
2643                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2644                                     -360.0, 360.0, 1.0, 10.0,
2645                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2646                                     box3d_vpz_angle_changed,
2647                                     0.1, 1);
2649     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2650     g_object_set_data(holder, "box3d_angle_z_action", eact);
2651     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2652         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2653     } else {
2654         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2655     }
2657     /* toggle VP in Z direction */
2658     {
2659     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2660                                                  _("Toggle VP in Z direction"),
2661                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (= parallel)"),
2662                                                  "toggle_vp_z",
2663                                                  Inkscape::ICON_SIZE_DECORATION);
2664     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2666     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Z));
2667     g_object_set_data(holder, "toggle_vp_z_action", act);
2668     /* we connect the signal after setting the state to avoid switching the state again */
2669     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_z_changed), holder);
2670     }
2672     sigc::connection *connection = new sigc::connection(
2673         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2674        );
2675     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2676     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2679 //########################
2680 //##       Spiral       ##
2681 //########################
2683 static void
2684 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2686     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2688     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2689         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2690     }
2692     // quit if run by the attr_changed listener
2693     if (g_object_get_data( tbl, "freeze" )) {
2694         return;
2695     }
2697     // in turn, prevent listener from responding
2698     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2700     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2702     bool modmade = false;
2703     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2704          items != NULL;
2705          items = items->next)
2706     {
2707         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2708             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2709             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2710             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2711             modmade = true;
2712         }
2713     }
2715     g_free(namespaced_name);
2717     if (modmade) {
2718         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2719                                    _("Change spiral"));
2720     }
2722     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2725 static void
2726 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2728     sp_spl_tb_value_changed(adj, tbl, "revolution");
2731 static void
2732 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2734     sp_spl_tb_value_changed(adj, tbl, "expansion");
2737 static void
2738 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2740     sp_spl_tb_value_changed(adj, tbl, "t0");
2743 static void
2744 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2746     GtkWidget *tbl = GTK_WIDGET(obj);
2748     GtkAdjustment *adj;
2750     // fixme: make settable
2751     gdouble rev = 5;
2752     gdouble exp = 1.0;
2753     gdouble t0 = 0.0;
2755     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2756     gtk_adjustment_set_value(adj, rev);
2757     gtk_adjustment_value_changed(adj);
2759     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2760     gtk_adjustment_set_value(adj, exp);
2761     gtk_adjustment_value_changed(adj);
2763     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2764     gtk_adjustment_set_value(adj, t0);
2765     gtk_adjustment_value_changed(adj);
2767     spinbutton_defocus(GTK_OBJECT(tbl));
2771 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2772                                          gchar const */*old_value*/, gchar const */*new_value*/,
2773                                          bool /*is_interactive*/, gpointer data)
2775     GtkWidget *tbl = GTK_WIDGET(data);
2777     // quit if run by the _changed callbacks
2778     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2779         return;
2780     }
2782     // in turn, prevent callbacks from responding
2783     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2785     GtkAdjustment *adj;
2786     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2787     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2789     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2790     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2792     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2793     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2795     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2799 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2800     NULL, /* child_added */
2801     NULL, /* child_removed */
2802     spiral_tb_event_attr_changed,
2803     NULL, /* content_changed */
2804     NULL  /* order_changed */
2805 };
2807 static void
2808 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2810     int n_selected = 0;
2811     Inkscape::XML::Node *repr = NULL;
2813     purge_repr_listener( tbl, tbl );
2815     for (GSList const *items = selection->itemList();
2816          items != NULL;
2817          items = items->next)
2818     {
2819         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2820             n_selected++;
2821             repr = SP_OBJECT_REPR((SPItem *) items->data);
2822         }
2823     }
2825     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2827     if (n_selected == 0) {
2828         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2829     } else if (n_selected == 1) {
2830         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2832         if (repr) {
2833             g_object_set_data( tbl, "repr", repr );
2834             Inkscape::GC::anchor(repr);
2835             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2836             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2837         }
2838     } else {
2839         // FIXME: implement averaging of all parameters for multiple selected
2840         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2841         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2842     }
2846 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2848     EgeAdjustmentAction* eact = 0;
2850     {
2851         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2852         ege_output_action_set_use_markup( act, TRUE );
2853         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2854         g_object_set_data( holder, "mode_action", act );
2855     }
2857     /* Revolution */
2858     {
2859         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2860         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2861         eact = create_adjustment_action( "SpiralRevolutionAction",
2862                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2863                                          "tools.shapes.spiral", "revolution", 3.0,
2864                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2865                                          0.01, 1024.0, 0.1, 1.0,
2866                                          labels, values, G_N_ELEMENTS(labels),
2867                                          sp_spl_tb_revolution_value_changed, 1, 2);
2868         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2869     }
2871     /* Expansion */
2872     {
2873         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2874         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2875         eact = create_adjustment_action( "SpiralExpansionAction",
2876                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2877                                          "tools.shapes.spiral", "expansion", 1.0,
2878                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2879                                          0.0, 1000.0, 0.01, 1.0,
2880                                          labels, values, G_N_ELEMENTS(labels),
2881                                          sp_spl_tb_expansion_value_changed);
2882         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2883     }
2885     /* T0 */
2886     {
2887         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2888         gdouble values[] = {0, 0.5, 0.9};
2889         eact = create_adjustment_action( "SpiralT0Action",
2890                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2891                                          "tools.shapes.spiral", "t0", 0.0,
2892                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2893                                          0.0, 0.999, 0.01, 1.0,
2894                                          labels, values, G_N_ELEMENTS(labels),
2895                                          sp_spl_tb_t0_value_changed);
2896         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2897     }
2899     /* Reset */
2900     {
2901         InkAction* inky = ink_action_new( "SpiralResetAction",
2902                                           _("Defaults"),
2903                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2904                                           GTK_STOCK_CLEAR,
2905                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2906         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2907         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2908     }
2911     sigc::connection *connection = new sigc::connection(
2912         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2913         );
2914     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2915     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2918 //########################
2919 //##     Pen/Pencil    ##
2920 //########################
2923 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2925     // Put stuff here
2928 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2930     // Put stuff here
2933 //########################
2934 //##       Tweak        ##
2935 //########################
2937 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2939     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2942 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2944     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2947 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2949     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2952 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2954     int mode = ege_select_one_action_get_active( act );
2955     prefs_set_int_attribute("tools.tweak", "mode", mode);
2957     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2958     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2959     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2960     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2961     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2962     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2963     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2964         if (doh) gtk_action_set_sensitive (doh, TRUE);
2965         if (dos) gtk_action_set_sensitive (dos, TRUE);
2966         if (dol) gtk_action_set_sensitive (dol, TRUE);
2967         if (doo) gtk_action_set_sensitive (doo, TRUE);
2968         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2969         if (fid) gtk_action_set_sensitive (fid, FALSE);
2970     } else {
2971         if (doh) gtk_action_set_sensitive (doh, FALSE);
2972         if (dos) gtk_action_set_sensitive (dos, FALSE);
2973         if (dol) gtk_action_set_sensitive (dol, FALSE);
2974         if (doo) gtk_action_set_sensitive (doo, FALSE);
2975         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2976         if (fid) gtk_action_set_sensitive (fid, TRUE);
2977     }
2980 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2982     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2985 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2986     bool show = gtk_toggle_action_get_active( act );
2987     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2989 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2990     bool show = gtk_toggle_action_get_active( act );
2991     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2993 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
2994     bool show = gtk_toggle_action_get_active( act );
2995     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2997 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
2998     bool show = gtk_toggle_action_get_active( act );
2999     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3002 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3004     {
3005         /* Width */
3006         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3007         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3008         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3009                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3010                                                               "tools.tweak", "width", 15,
3011                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3012                                                               1, 100, 1.0, 10.0,
3013                                                               labels, values, G_N_ELEMENTS(labels),
3014                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3015         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3016         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3017     }
3020     {
3021         /* Force */
3022         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3023         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3024         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3025                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3026                                                               "tools.tweak", "force", 20,
3027                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3028                                                               1, 100, 1.0, 10.0,
3029                                                               labels, values, G_N_ELEMENTS(labels),
3030                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3031         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3032         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3033     }
3035     /* Mode */
3036     {
3037         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3039         GtkTreeIter iter;
3040         gtk_list_store_append( model, &iter );
3041         gtk_list_store_set( model, &iter,
3042                             0, _("Push mode"),
3043                             1, _("Push parts of paths in any direction"),
3044                             2, "tweak_push_mode",
3045                             -1 );
3047         gtk_list_store_append( model, &iter );
3048         gtk_list_store_set( model, &iter,
3049                             0, _("Shrink mode"),
3050                             1, _("Shrink (inset) parts of paths"),
3051                             2, "tweak_shrink_mode",
3052                             -1 );
3054         gtk_list_store_append( model, &iter );
3055         gtk_list_store_set( model, &iter,
3056                             0, _("Grow mode"),
3057                             1, _("Grow (outset) parts of paths"),
3058                             2, "tweak_grow_mode",
3059                             -1 );
3061         gtk_list_store_append( model, &iter );
3062         gtk_list_store_set( model, &iter,
3063                             0, _("Attract mode"),
3064                             1, _("Attract parts of paths towards cursor"),
3065                             2, "tweak_attract_mode",
3066                             -1 );
3068         gtk_list_store_append( model, &iter );
3069         gtk_list_store_set( model, &iter,
3070                             0, _("Repel mode"),
3071                             1, _("Repel parts of paths from cursor"),
3072                             2, "tweak_repel_mode",
3073                             -1 );
3075         gtk_list_store_append( model, &iter );
3076         gtk_list_store_set( model, &iter,
3077                             0, _("Roughen mode"),
3078                             1, _("Roughen parts of paths"),
3079                             2, "tweak_roughen_mode",
3080                             -1 );
3082         gtk_list_store_append( model, &iter );
3083         gtk_list_store_set( model, &iter,
3084                             0, _("Color paint mode"),
3085                             1, _("Paint the tool's color upon selected objects"),
3086                             2, "tweak_colorpaint_mode",
3087                             -1 );
3089         gtk_list_store_append( model, &iter );
3090         gtk_list_store_set( model, &iter,
3091                             0, _("Color jitter mode"),
3092                             1, _("Jitter the colors of selected objects"),
3093                             2, "tweak_colorjitter_mode",
3094                             -1 );
3096         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3097         g_object_set( act, "short_label", _("Mode:"), NULL );
3098         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3099         g_object_set_data( holder, "mode_action", act );
3101         ege_select_one_action_set_appearance( act, "full" );
3102         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3103         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3104         ege_select_one_action_set_icon_column( act, 2 );
3105         ege_select_one_action_set_tooltip_column( act, 1  );
3107         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3108         ege_select_one_action_set_active( act, mode );
3109         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3111         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3112     }
3114     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3116     {
3117         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3118         ege_output_action_set_use_markup( act, TRUE );
3119         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3120         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3121             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3122         g_object_set_data( holder, "tweak_channels_label", act);
3123     }
3125     {
3126         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3127                                                       _("Hue"),
3128                                                       _("In color mode, act on objects' hue"),
3129                                                       NULL,
3130                                                       Inkscape::ICON_SIZE_DECORATION );
3131         g_object_set( act, "short_label", _("H"), NULL );
3132         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3133         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3134         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3135         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3136             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3137         g_object_set_data( holder, "tweak_doh", act);
3138     }
3139     {
3140         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3141                                                       _("Saturation"),
3142                                                       _("In color mode, act on objects' saturation"),
3143                                                       NULL,
3144                                                       Inkscape::ICON_SIZE_DECORATION );
3145         g_object_set( act, "short_label", _("S"), NULL );
3146         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3147         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3148         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3149         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3150             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3151         g_object_set_data( holder, "tweak_dos", act );
3152     }
3153     {
3154         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3155                                                       _("Lightness"),
3156                                                       _("In color mode, act on objects' lightness"),
3157                                                       NULL,
3158                                                       Inkscape::ICON_SIZE_DECORATION );
3159         g_object_set( act, "short_label", _("L"), NULL );
3160         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3161         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3162         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3163         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3164             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3165         g_object_set_data( holder, "tweak_dol", act );
3166     }
3167     {
3168         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3169                                                       _("Opacity"),
3170                                                       _("In color mode, act on objects' opacity"),
3171                                                       NULL,
3172                                                       Inkscape::ICON_SIZE_DECORATION );
3173         g_object_set( act, "short_label", _("O"), NULL );
3174         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3175         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3176         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3177         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3178             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3179         g_object_set_data( holder, "tweak_doo", act );
3180     }
3182     {   /* Fidelity */
3183         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3184         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3185         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3186                                                               _("Fidelity"), _("Fidelity:"),
3187                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3188                                                               "tools.tweak", "fidelity", 50,
3189                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3190                                                               1, 100, 1.0, 10.0,
3191                                                               labels, values, G_N_ELEMENTS(labels),
3192                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3193         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3194         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3195         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3196             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3197         g_object_set_data( holder, "tweak_fidelity", eact );
3198     }
3201     /* Use Pressure button */
3202     {
3203         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3204                                                       _("Pressure"),
3205                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3206                                                       "use_pressure",
3207                                                       Inkscape::ICON_SIZE_DECORATION );
3208         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3209         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3210         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3211     }
3216 //########################
3217 //##     Calligraphy    ##
3218 //########################
3220 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3222     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3225 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3227     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3230 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3232     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3235 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3237     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3240 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3242     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3245 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3247     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3250 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3252     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3255 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3257     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3260 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3262     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3265 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3267     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3270 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3272     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3274     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3277 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3279     // FIXME: make defaults settable via Inkscape Options
3280     struct KeyValue {
3281         char const *key;
3282         double value;
3283     } const key_values[] = {
3284         {"mass", 0.02},
3285         {"wiggle", 0.0},
3286         {"angle", 30.0},
3287         {"width", 15},
3288         {"thinning", 0.1},
3289         {"tremor", 0.0},
3290         {"flatness", 0.9},
3291         {"cap_rounding", 0.0}
3292     };
3294     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3295         KeyValue const &kv = key_values[i];
3296         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3297         if ( adj ) {
3298             gtk_adjustment_set_value(adj, kv.value);
3299         }
3300     }
3304 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3306     {
3307         EgeAdjustmentAction* calligraphy_angle = 0;
3309         {
3310         /* Width */
3311         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3312         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3313         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3314                                                               _("Pen Width"), _("Width:"),
3315                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3316                                                               "tools.calligraphic", "width", 15,
3317                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3318                                                               1, 100, 1.0, 10.0,
3319                                                               labels, values, G_N_ELEMENTS(labels),
3320                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3321         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3322         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3323         }
3325         {
3326         /* Thinning */
3327             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3328             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3329         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3330                                                               _("Stroke Thinning"), _("Thinning:"),
3331                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3332                                                               "tools.calligraphic", "thinning", 0.1,
3333                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3334                                                               -1.0, 1.0, 0.01, 0.1,
3335                                                               labels, values, G_N_ELEMENTS(labels),
3336                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3337         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3338         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3339         }
3341         {
3342         /* Angle */
3343         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3344         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3345         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3346                                                               _("Pen Angle"), _("Angle:"),
3347                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3348                                                               "tools.calligraphic", "angle", 30,
3349                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3350                                                               -90.0, 90.0, 1.0, 10.0,
3351                                                               labels, values, G_N_ELEMENTS(labels),
3352                                                               sp_ddc_angle_value_changed, 1, 0 );
3353         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3354         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3355         calligraphy_angle = eact;
3356         }
3358         {
3359         /* Fixation */
3360             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3361         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3362         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3363                                                               _("Fixation"), _("Fixation:"),
3364                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3365                                                               "tools.calligraphic", "flatness", 0.9,
3366                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3367                                                               0.0, 1.0, 0.01, 0.1,
3368                                                               labels, values, G_N_ELEMENTS(labels),
3369                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3370         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3371         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3372         }
3374         {
3375         /* Cap Rounding */
3376             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3377         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3378         // TRANSLATORS: "cap" means "end" (both start and finish) here
3379         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3380                                                               _("Cap rounding"), _("Caps:"),
3381                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3382                                                               "tools.calligraphic", "cap_rounding", 0.0,
3383                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3384                                                               0.0, 5.0, 0.01, 0.1,
3385                                                               labels, values, G_N_ELEMENTS(labels),
3386                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3387         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3388         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3389         }
3391         {
3392         /* Tremor */
3393             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3394         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3395         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3396                                                               _("Stroke Tremor"), _("Tremor:"),
3397                                                               _("Increase to make strokes rugged and trembling"),
3398                                                               "tools.calligraphic", "tremor", 0.0,
3399                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3400                                                               0.0, 1.0, 0.01, 0.1,
3401                                                               labels, values, G_N_ELEMENTS(labels),
3402                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3404         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3405         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3406         }
3408         {
3409         /* Wiggle */
3410         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3411         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3412         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3413                                                               _("Pen Wiggle"), _("Wiggle:"),
3414                                                               _("Increase to make the pen waver and wiggle"),
3415                                                               "tools.calligraphic", "wiggle", 0.0,
3416                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3417                                                               0.0, 1.0, 0.01, 0.1,
3418                                                               labels, values, G_N_ELEMENTS(labels),
3419                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3420         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3421         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3422         }
3424         {
3425         /* Mass */
3426             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3427         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3428         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3429                                                               _("Pen Mass"), _("Mass:"),
3430                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3431                                                               "tools.calligraphic", "mass", 0.02,
3432                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3433                                                               0.0, 1.0, 0.01, 0.1,
3434                                                               labels, values, G_N_ELEMENTS(labels),
3435                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3436         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3437         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3438         }
3441         /* Trace Background button */
3442         {
3443             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3444                                                           _("Trace Background"),
3445                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3446                                                           "trace_background",
3447                                                           Inkscape::ICON_SIZE_DECORATION );
3448             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3449             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3450             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3451         }
3453         /* Use Pressure button */
3454         {
3455             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3456                                                           _("Pressure"),
3457                                                           _("Use the pressure of the input device to alter the width of the pen"),
3458                                                           "use_pressure",
3459                                                           Inkscape::ICON_SIZE_DECORATION );
3460             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3461             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3462             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3463         }
3465         /* Use Tilt button */
3466         {
3467             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3468                                                           _("Tilt"),
3469                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3470                                                           "use_tilt",
3471                                                           Inkscape::ICON_SIZE_DECORATION );
3472             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3473             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3474             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3475             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3476         }
3478         /* Reset */
3479         {
3480             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3481                                              _("Defaults"),
3482                                              _("Reset all parameters to defaults"),
3483                                              GTK_STOCK_CLEAR );
3484             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3485             gtk_action_group_add_action( mainActions, act );
3486             gtk_action_set_sensitive( act, TRUE );
3487         }
3488     }
3492 //########################
3493 //##    Circle / Arc    ##
3494 //########################
3496 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3498     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3499     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3501     if (v1 == 0 && v2 == 0) {
3502         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3503             gtk_action_set_sensitive( ocb, FALSE );
3504             gtk_action_set_sensitive( make_whole, FALSE );
3505         }
3506     } else {
3507         gtk_action_set_sensitive( ocb, TRUE );
3508         gtk_action_set_sensitive( make_whole, TRUE );
3509     }
3512 static void
3513 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3515     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3517     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3518         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3519     }
3521     // quit if run by the attr_changed listener
3522     if (g_object_get_data( tbl, "freeze" )) {
3523         return;
3524     }
3526     // in turn, prevent listener from responding
3527     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3529     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3531     bool modmade = false;
3532     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3533          items != NULL;
3534          items = items->next)
3535     {
3536         SPItem *item = SP_ITEM(items->data);
3538         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3540             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3541             SPArc *arc = SP_ARC(item);
3543             if (!strcmp(value_name, "start"))
3544                 ge->start = (adj->value * M_PI)/ 180;
3545             else
3546                 ge->end = (adj->value * M_PI)/ 180;
3548             sp_genericellipse_normalize(ge);
3549             ((SPObject *)arc)->updateRepr();
3550             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3552             modmade = true;
3553         }
3554     }
3556     g_free(namespaced_name);
3558     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3560     sp_arctb_sensitivize( tbl, adj->value, other->value );
3562     if (modmade) {
3563         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3564                                    _("Arc: Change start/end"));
3565     }
3567     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3571 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3573     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3576 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3578     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3581 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3583     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3584     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3585         if ( ege_select_one_action_get_active( act ) != 0 ) {
3586             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3587         } else {
3588             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3589         }
3590     }
3592     // quit if run by the attr_changed listener
3593     if (g_object_get_data( tbl, "freeze" )) {
3594         return;
3595     }
3597     // in turn, prevent listener from responding
3598     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3600     bool modmade = false;
3602     if ( ege_select_one_action_get_active(act) != 0 ) {
3603         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3604              items != NULL;
3605              items = items->next)
3606         {
3607             if (SP_IS_ARC((SPItem *) items->data)) {
3608                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3609                 repr->setAttribute("sodipodi:open", "true");
3610                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3611                 modmade = true;
3612             }
3613         }
3614     } else {
3615         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3616              items != NULL;
3617              items = items->next)
3618         {
3619             if (SP_IS_ARC((SPItem *) items->data))    {
3620                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3621                 repr->setAttribute("sodipodi:open", NULL);
3622                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3623                 modmade = true;
3624             }
3625         }
3626     }
3628     if (modmade) {
3629         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3630                                    _("Arc: Change open/closed"));
3631     }
3633     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3636 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3638     GtkAdjustment *adj;
3639     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3640     gtk_adjustment_set_value(adj, 0.0);
3641     gtk_adjustment_value_changed(adj);
3643     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3644     gtk_adjustment_set_value(adj, 0.0);
3645     gtk_adjustment_value_changed(adj);
3647     spinbutton_defocus( GTK_OBJECT(obj) );
3650 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3651                                       gchar const */*old_value*/, gchar const */*new_value*/,
3652                                       bool /*is_interactive*/, gpointer data)
3654     GObject *tbl = G_OBJECT(data);
3656     // quit if run by the _changed callbacks
3657     if (g_object_get_data( tbl, "freeze" )) {
3658         return;
3659     }
3661     // in turn, prevent callbacks from responding
3662     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3664     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3665     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3667     GtkAdjustment *adj1,*adj2;
3668     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3669     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3670     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3671     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3673     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3675     char const *openstr = NULL;
3676     openstr = repr->attribute("sodipodi:open");
3677     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3679     if (openstr) {
3680         ege_select_one_action_set_active( ocb, 1 );
3681     } else {
3682         ege_select_one_action_set_active( ocb, 0 );
3683     }
3685     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3688 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3689     NULL, /* child_added */
3690     NULL, /* child_removed */
3691     arc_tb_event_attr_changed,
3692     NULL, /* content_changed */
3693     NULL  /* order_changed */
3694 };
3697 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3699     int n_selected = 0;
3700     Inkscape::XML::Node *repr = NULL;
3702     purge_repr_listener( tbl, tbl );
3704     for (GSList const *items = selection->itemList();
3705          items != NULL;
3706          items = items->next)
3707     {
3708         if (SP_IS_ARC((SPItem *) items->data)) {
3709             n_selected++;
3710             repr = SP_OBJECT_REPR((SPItem *) items->data);
3711         }
3712     }
3714     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3716     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3717     if (n_selected == 0) {
3718         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3719     } else if (n_selected == 1) {
3720         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3721         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3723         if (repr) {
3724             g_object_set_data( tbl, "repr", repr );
3725             Inkscape::GC::anchor(repr);
3726             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3727             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3728         }
3729     } else {
3730         // FIXME: implement averaging of all parameters for multiple selected
3731         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3732         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3733         sp_arctb_sensitivize( tbl, 1, 0 );
3734     }
3738 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3740     EgeAdjustmentAction* eact = 0;
3743     {
3744         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3745         ege_output_action_set_use_markup( act, TRUE );
3746         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3747         g_object_set_data( holder, "mode_action", act );
3748     }
3750     /* Start */
3751     {
3752         eact = create_adjustment_action( "ArcStartAction",
3753                                          _("Start"), _("Start:"),
3754                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3755                                          "tools.shapes.arc", "start", 0.0,
3756                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3757                                          -360.0, 360.0, 1.0, 10.0,
3758                                          0, 0, 0,
3759                                          sp_arctb_start_value_changed);
3760         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3761     }
3763     /* End */
3764     {
3765         eact = create_adjustment_action( "ArcEndAction",
3766                                          _("End"), _("End:"),
3767                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3768                                          "tools.shapes.arc", "end", 0.0,
3769                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3770                                          -360.0, 360.0, 1.0, 10.0,
3771                                          0, 0, 0,
3772                                          sp_arctb_end_value_changed);
3773         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3774     }
3776     /* Segments / Pie checkbox */
3777     {
3778         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3780         GtkTreeIter iter;
3781         gtk_list_store_append( model, &iter );
3782         gtk_list_store_set( model, &iter,
3783                             0, _("Closed arc"),
3784                             1, _("Switch to segment (closed shape with two radii)"),
3785                             2, "circle_closed_arc",
3786                             -1 );
3788         gtk_list_store_append( model, &iter );
3789         gtk_list_store_set( model, &iter,
3790                             0, _("Open Arc"),
3791                             1, _("Switch to arc (unclosed shape)"),
3792                             2, "circle_open_arc",
3793                             -1 );
3795         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3796         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3797         g_object_set_data( holder, "open_action", act );
3799         ege_select_one_action_set_appearance( act, "full" );
3800         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3801         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3802         ege_select_one_action_set_icon_column( act, 2 );
3803         ege_select_one_action_set_tooltip_column( act, 1  );
3805         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3806         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3807         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3808         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3809     }
3811     /* Make Whole */
3812     {
3813         InkAction* inky = ink_action_new( "ArcResetAction",
3814                                           _("Make whole"),
3815                                           _("Make the shape a whole ellipse, not arc or segment"),
3816                                           "reset_circle",
3817                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3818         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3819         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3820         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3821         g_object_set_data( holder, "make_whole", inky );
3822     }
3824     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3825     // sensitivize make whole and open checkbox
3826     {
3827         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3828         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3829         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3830     }
3833     sigc::connection *connection = new sigc::connection(
3834         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3835         );
3836     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3837     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3843 // toggle button callbacks and updaters
3845 //########################
3846 //##      Dropper       ##
3847 //########################
3849 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3850     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3851     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3852     if ( set_action ) {
3853         if ( gtk_toggle_action_get_active( act ) ) {
3854             gtk_action_set_sensitive( set_action, TRUE );
3855         } else {
3856             gtk_action_set_sensitive( set_action, FALSE );
3857         }
3858     }
3860     spinbutton_defocus(GTK_OBJECT(tbl));
3863 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3864     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3865     spinbutton_defocus(GTK_OBJECT(tbl));
3869 /**
3870  * Dropper auxiliary toolbar construction and setup.
3871  *
3872  * TODO: Would like to add swatch of current color.
3873  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3874  *       can drag and drop places. Will provide a nice mixing palette.
3875  */
3876 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3878     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3880     {
3881         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3882                                                       _("Pick alpha"),
3883                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3884                                                       "color_alpha_get",
3885                                                       Inkscape::ICON_SIZE_DECORATION );
3886         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3887         g_object_set_data( holder, "pick_action", act );
3888         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3889         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3890     }
3892     {
3893         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3894                                                       _("Set alpha"),
3895                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3896                                                       "color_alpha_set",
3897                                                       Inkscape::ICON_SIZE_DECORATION );
3898         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3899         g_object_set_data( holder, "set_action", act );
3900         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3901         // make sure it's disabled if we're not picking alpha
3902         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3903         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3904     }
3908 //########################
3909 //##    Text Toolbox    ##
3910 //########################
3911 /*
3912 static void
3913 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3915     //Call back for letter sizing spinbutton
3918 static void
3919 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3921     //Call back for line height spinbutton
3924 static void
3925 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3927     //Call back for horizontal kerning spinbutton
3930 static void
3931 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3933     //Call back for vertical kerning spinbutton
3936 static void
3937 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3939     //Call back for letter rotation spinbutton
3940 }*/
3942 namespace {
3944 bool visible = false;
3946 void
3947 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3949     SPStyle *query =
3950         sp_style_new (SP_ACTIVE_DOCUMENT);
3951     
3952     int result_fontspec =
3953         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3955     int result_family =
3956         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3958     int result_style =
3959         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3961     int result_numbers =
3962         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3964     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3966     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3967     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3968     {
3969         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3971         if (repr)
3972         {
3973             sp_style_read_from_repr (query, repr);
3974         }
3975         else
3976         {
3977             return;
3978         }
3979     }
3981     if (query->text)
3982     {
3983         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3984             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3985             gtk_entry_set_text (GTK_ENTRY (entry), "");
3987         } else if (query->text->font_specification.value || query->text->font_family.value) {
3989             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3990             
3991             // Get the font that corresponds 
3992             Glib::ustring familyName;
3993             
3994             font_instance * font = font_factory::Default()->FaceFromStyle(query);
3995             if (font) {
3996                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
3997                 font->Unref();
3998                 font = NULL;
3999             }
4000             
4001             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4003             Gtk::TreePath path;
4004             try {
4005                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4006             } catch (...) {
4007                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4008                 return;
4009             }
4011             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4012             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4014             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4016             gtk_tree_selection_select_path (tselection, path.gobj());
4017             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4019             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4020         }
4022         //Size
4023         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4024         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4025         g_object_set_data (tbl, "size-block", gpointer(1));
4026         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4027         g_object_set_data (tbl, "size-block", gpointer(0));
4028         free (str);
4030         //Anchor
4031         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4032         {
4033             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4034             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4035             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4036             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4037         }
4038         else
4039         {
4040             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4041             {
4042                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4043                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4044                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4045                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4046             }
4047             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4048             {
4049                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4050                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4051                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4052                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4053             }
4054             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4055             {
4056                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4057                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4058                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4059                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4060             }
4061         }
4063         //Style
4064         {
4065             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4067             gboolean active = gtk_toggle_button_get_active (button);
4068             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4070             if (active != check)
4071             {
4072                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4073                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4074                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4075             }
4076         }
4078         {
4079             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4081             gboolean active = gtk_toggle_button_get_active (button);
4082             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4084             if (active != check)
4085             {
4086                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4087                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4088                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4089             }
4090         }
4092         //Orientation
4093         //locking both buttons, changing one affect all group (both)
4094         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4095         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4097         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4098         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4100         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4101         {
4102             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4103         }
4104         else
4105         {
4106             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4107         }
4108         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4109         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4110     }
4112     sp_style_unref(query);
4115 void
4116 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4118     sp_text_toolbox_selection_changed (selection, tbl);
4121 void
4122 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4124     sp_text_toolbox_selection_changed (NULL, tbl);
4127 void
4128 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4129                                 GObject             *tbl)
4131     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4132     GtkTreeModel *model = 0;
4133     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4134     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4135     GtkTreeIter   iter;
4136     char         *family = 0;
4138     (void)popdown;
4140     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4141     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4143     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4144         return;
4145     }
4147     gtk_tree_model_get (model, &iter, 0, &family, -1);
4149     if (g_object_get_data (G_OBJECT (selection), "block"))
4150     {
4151         gtk_entry_set_text (GTK_ENTRY (entry), family);
4152         return;
4153     }
4155     gtk_entry_set_text (GTK_ENTRY (entry), family);
4157     SPStyle *query =
4158         sp_style_new (SP_ACTIVE_DOCUMENT);
4160     int result_fontspec =
4161         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4162     
4163     SPCSSAttr *css = sp_repr_css_attr_new ();
4164     
4165     std::string fontSpec = query->text->font_specification.value;
4166     if (!fontSpec.empty()) {
4167         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4168         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4169             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4170             if (font) {
4171                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4172                 
4173                 // Set all the these just in case they were altered when finding the best
4174                 // match for the new family and old style...
4175                 
4176                 gchar c[256];
4177                 
4178                 font->Family(c, 256);
4179                 sp_repr_css_set_property (css, "font-family", c);
4180                 
4181                 font->Attribute( "weight", c, 256);
4182                 sp_repr_css_set_property (css, "font-weight", c);
4183                 
4184                 font->Attribute("style", c, 256);
4185                 sp_repr_css_set_property (css, "font-style", c);
4186                 
4187                 font->Attribute("stretch", c, 256);
4188                 sp_repr_css_set_property (css, "font-stretch", c);
4189                 
4190                 font->Attribute("variant", c, 256);
4191                 sp_repr_css_set_property (css, "font-variant", c);
4192                 
4193                 font->Unref();
4194             }
4195         }
4196     }
4198     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4199     if (result_fontspec == QUERY_STYLE_NOTHING)
4200     {
4201         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4202         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4203     }
4204     else
4205     {
4206         sp_desktop_set_style (desktop, css, true, true);
4207     }
4209     sp_style_unref(query);
4211     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4212                                    _("Text: Change font family"));
4213     sp_repr_css_attr_unref (css);
4214     free (family);
4215     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4217     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4220 void
4221 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4222                                        GObject      *tbl)
4224     const char *family = gtk_entry_get_text (entry);
4226     try {
4227         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4228         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4229         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4230         gtk_tree_selection_select_path (selection, path.gobj());
4231         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4232         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4233     } catch (...) {
4234         if (family && strlen (family))
4235         {
4236             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4237         }
4238     }
4241 void
4242 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4243                                    gpointer          data)
4245     if (g_object_get_data (G_OBJECT (button), "block")) return;
4246     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4247     int prop = GPOINTER_TO_INT(data);
4249     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4250     SPCSSAttr *css = sp_repr_css_attr_new ();
4252     switch (prop)
4253     {
4254         case 0:
4255         {
4256             sp_repr_css_set_property (css, "text-anchor", "start");
4257             sp_repr_css_set_property (css, "text-align", "start");
4258             break;
4259         }
4260         case 1:
4261         {
4262             sp_repr_css_set_property (css, "text-anchor", "middle");
4263             sp_repr_css_set_property (css, "text-align", "center");
4264             break;
4265         }
4267         case 2:
4268         {
4269             sp_repr_css_set_property (css, "text-anchor", "end");
4270             sp_repr_css_set_property (css, "text-align", "end");
4271             break;
4272         }
4274         case 3:
4275         {
4276             sp_repr_css_set_property (css, "text-anchor", "start");
4277             sp_repr_css_set_property (css, "text-align", "justify");
4278             break;
4279         }
4280     }
4282     SPStyle *query =
4283         sp_style_new (SP_ACTIVE_DOCUMENT);
4284     int result_numbers =
4285         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4287     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4288     if (result_numbers == QUERY_STYLE_NOTHING)
4289     {
4290         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4291     }
4293     sp_style_unref(query);
4295     sp_desktop_set_style (desktop, css, true, true);
4296     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4297                                    _("Text: Change alignment"));
4298     sp_repr_css_attr_unref (css);
4300     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4303 void
4304 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4305                                gpointer          data)
4307     if (g_object_get_data (G_OBJECT (button), "block")) return;
4309     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4310     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4311     int          prop       = GPOINTER_TO_INT(data);
4312     bool         active     = gtk_toggle_button_get_active (button);
4314     SPStyle *query =
4315         sp_style_new (SP_ACTIVE_DOCUMENT);
4316     int result_fontspec =
4317         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4318     
4319     Glib::ustring fontSpec = query->text->font_specification.value;
4320     Glib::ustring newFontSpec;
4322     switch (prop)
4323     {
4324         case 0:
4325         {
4326             if (!fontSpec.empty()) {
4327                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4328             }
4329             if (fontSpec != newFontSpec) {
4330                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4331             }
4332             break;
4333         }
4335         case 1:
4336         {
4337             if (!fontSpec.empty()) {
4338                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4339             }
4340             if (fontSpec != newFontSpec) {
4341                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4342             }
4343             break;
4344         }
4345     }
4347     if (!fontSpec.empty()) {
4348         sp_repr_css_set_property (css, "-inkscape-font-specification", fontSpec.c_str()); 
4349     }
4351     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4352     if (result_fontspec == 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_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4361                                    _("Text: Change font style"));
4362     sp_repr_css_attr_unref (css);
4364     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4367 void
4368 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4369                                      gpointer         data)
4371     if (g_object_get_data (G_OBJECT (button), "block")) {
4372         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4373         return;
4374     }
4376     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4377     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4378     int          prop       = GPOINTER_TO_INT(data);
4380     switch (prop)
4381     {
4382         case 0:
4383         {
4384             sp_repr_css_set_property (css, "writing-mode", "lr");
4385             break;
4386         }
4388         case 1:
4389         {
4390             sp_repr_css_set_property (css, "writing-mode", "tb");
4391             break;
4392         }
4393     }
4395     SPStyle *query =
4396         sp_style_new (SP_ACTIVE_DOCUMENT);
4397     int result_numbers =
4398         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4400     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4401     if (result_numbers == QUERY_STYLE_NOTHING)
4402     {
4403         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4404     }
4406     sp_desktop_set_style (desktop, css, true, true);
4407     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4408                                    _("Text: Change orientation"));
4409     sp_repr_css_attr_unref (css);
4411     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4414 gboolean
4415 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4417     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4418     if (!desktop) return FALSE;
4420     switch (get_group0_keyval (event)) {
4421         case GDK_Escape: // defocus
4422             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4423             return TRUE; // I consumed the event
4424             break;
4425         case GDK_Return: // defocus
4426         case GDK_KP_Enter:
4427             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4428             return TRUE; // I consumed the event
4429             break;
4430     }
4431     return FALSE;
4434 gboolean
4435 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4437     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4438     if (!desktop) return FALSE;
4440     switch (get_group0_keyval (event)) {
4441         case GDK_Escape: // defocus
4442             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4443             sp_text_toolbox_selection_changed (NULL, tbl); // update
4444             return TRUE; // I consumed the event
4445             break;
4446     }
4447     return FALSE;
4450 gboolean
4451 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4453     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4454     if (!desktop) return FALSE;
4456     switch (get_group0_keyval (event)) {
4457         case GDK_KP_Enter:
4458         case GDK_Return:
4459         case GDK_Escape: // defocus
4460             gtk_widget_hide (w);
4461             visible = false;
4462             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4463             return TRUE; // I consumed the event
4464             break;
4465     }
4466     return FALSE;
4470 void
4471 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4472                                GObject     *tbl)
4474     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4476     if (g_object_get_data (tbl, "size-block")) return;
4478     char *text = gtk_combo_box_get_active_text (cbox);
4480     SPCSSAttr *css = sp_repr_css_attr_new ();
4481     sp_repr_css_set_property (css, "font-size", text);
4482     free (text);
4484     SPStyle *query =
4485         sp_style_new (SP_ACTIVE_DOCUMENT);
4486     int result_numbers =
4487         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4489     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4490     if (result_numbers == QUERY_STYLE_NOTHING)
4491     {
4492         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4493     }
4495     sp_style_unref(query);
4497     sp_desktop_set_style (desktop, css, true, true);
4498     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4499                                    _("Text: Change font size"));
4500     sp_repr_css_attr_unref (css);
4503     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4504         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4507 void
4508 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4509                                          GObject            *tbl)
4511     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4512     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4513     int x, y;
4515     if (!visible)
4516     {
4517         gdk_window_get_origin (widget->window, &x, &y);
4518         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4519         gtk_widget_show_all (popdown);
4521         gdk_pointer_grab (widget->window, TRUE,
4522                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4523                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4524                                         GDK_POINTER_MOTION_MASK),
4525                           NULL, NULL, GDK_CURRENT_TIME);
4527         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4529         visible = true;
4530     }
4531     else
4532     {
4533         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4534         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4535         gtk_widget_hide (popdown);
4536         visible = false;
4537     }
4540 gboolean
4541 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4542                                  GdkEventFocus    */*event*/,
4543                                  GObject          */*tbl*/)
4545     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4546     return FALSE;
4549 gboolean
4550 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4551                                    GdkEventFocus    */*event*/,
4552                                    GObject          */*tbl*/)
4554     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4556     gtk_widget_hide (popdown);
4557     visible = false;
4558     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4559     return TRUE;
4562 void
4563 cell_data_func  (GtkTreeViewColumn */*column*/,
4564                  GtkCellRenderer   *cell,
4565                  GtkTreeModel      *tree_model,
4566                  GtkTreeIter       *iter,
4567                  gpointer           /*data*/)
4569     char        *family,
4570         *family_escaped,
4571         *sample_escaped;
4573     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4575     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4577     family_escaped = g_markup_escape_text (family, -1);
4578     sample_escaped = g_markup_escape_text (sample, -1);
4580     std::stringstream markup;
4581     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4582     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4584     free (family);
4585     free (family_escaped);
4586     free (sample_escaped);
4589 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4590     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4591     if (completion) {
4592         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4593         g_object_unref (completion);
4594     }
4597 GtkWidget*
4598 sp_text_toolbox_new (SPDesktop *desktop)
4600     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4602     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4603     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4605     GtkTooltips *tt = gtk_tooltips_new();
4606     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4608     ////////////Family
4609     //Window
4610     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4611     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4613     //Entry
4614     GtkWidget           *entry = gtk_entry_new ();
4615     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4616     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4617     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4618     gtk_entry_completion_set_text_column (completion, 0);
4619     gtk_entry_completion_set_minimum_key_length (completion, 1);
4620     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4621     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4622     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4623     aux_toolbox_space (tbl, 1);
4624     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4625     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4627     //Button
4628     GtkWidget   *button = gtk_button_new ();
4629     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4630     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4632     //Popdown
4633     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4634     GtkWidget           *treeview = gtk_tree_view_new ();
4636     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4637     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4638     gtk_tree_view_column_pack_start (column, cell, FALSE);
4639     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4640     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4641     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4643     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4644     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4645     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4647     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4649     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4650     gtk_container_add (GTK_CONTAINER (sw), treeview);
4652     gtk_container_add (GTK_CONTAINER (window), sw);
4653     gtk_widget_set_size_request (window, 300, 450);
4655     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4656     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4657     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4659     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4661     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4662     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4664     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4665     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4667     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4668     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4669     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4670     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4671     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4673     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4674     aux_toolbox_space (tbl, 1);
4675     GtkWidget *box = gtk_event_box_new ();
4676     gtk_container_add (GTK_CONTAINER (box), image);
4677     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4678     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4679     GtkTooltips *tooltips = gtk_tooltips_new ();
4680     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4681     gtk_widget_hide (GTK_WIDGET (box));
4682     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4684     ////////////Size
4685     const char *sizes[] = {
4686         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4687         "16", "18", "20", "22", "24", "28",
4688         "32", "36", "40", "48", "56", "64", "72", "144"
4689     };
4691     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4692     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4693     gtk_widget_set_size_request (cbox, 80, -1);
4694     aux_toolbox_space (tbl, 1);
4695     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4696     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4697     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4698     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4700     //spacer
4701     aux_toolbox_space (tbl, 4);
4702     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4704     ////////////Text anchor
4705     GtkWidget *group   = gtk_radio_button_new (NULL);
4706     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4707     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4709     // left
4710     GtkWidget *rbutton = group;
4711     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4712     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4713     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4715     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4716     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4717     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4718     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4720     // center
4721     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4722     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4723     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4724     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4726     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4727     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4728     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4729     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4731     // right
4732     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4733     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4734     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4735     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4737     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4738     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4739     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4740     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4742     // fill
4743     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4744     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4745     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4746     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4748     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4749     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4750     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4751     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4753     aux_toolbox_space (tbl, 1);
4754     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4756     //spacer
4757     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4759     ////////////Text style
4760     row = gtk_hbox_new (FALSE, 4);
4762     // bold
4763     rbutton = gtk_toggle_button_new ();
4764     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4765     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4766     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4767     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4769     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4770     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4771     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4773     // italic
4774     rbutton = gtk_toggle_button_new ();
4775     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4776     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4777     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4778     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4780     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4781     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4782     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4784     aux_toolbox_space (tbl, 1);
4785     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4787     //spacer
4788     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4790     ////////////Text orientation
4791     group   = gtk_radio_button_new (NULL);
4792     row     = gtk_hbox_new (FALSE, 4);
4793     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4795     // horizontal
4796     rbutton = group;
4797     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4798     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4799     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4800     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4802     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4803     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4804     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4806     // vertical
4807     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4808     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4809     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4810     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4811     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4813     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4814     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4815     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4816     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4819     //watch selection
4820     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4822     sigc::connection *c_selection_changed =
4823         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4824                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4825     pool->add_connection ("selection-changed", c_selection_changed);
4827     sigc::connection *c_selection_modified =
4828         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4829                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4830     pool->add_connection ("selection-modified", c_selection_modified);
4832     sigc::connection *c_subselection_changed =
4833         new sigc::connection (desktop->connectToolSubselectionChanged
4834                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4835     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4837     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4840     gtk_widget_show_all (tbl);
4841     return tbl;
4843 } // end of sp_text_toolbox_new()
4845 }//<unnamed> namespace
4848 //#########################
4849 //##      Connector      ##
4850 //#########################
4852 static void sp_connector_path_set_avoid(void)
4854     cc_selection_set_avoid(true);
4858 static void sp_connector_path_set_ignore(void)
4860     cc_selection_set_avoid(false);
4865 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4867     // quit if run by the _changed callbacks
4868     if (g_object_get_data( tbl, "freeze" )) {
4869         return;
4870     }
4872     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4873     SPDocument *doc = sp_desktop_document(desktop);
4875     if (!sp_document_get_undo_sensitive(doc))
4876     {
4877         return;
4878     }
4880     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4882     if ( repr->attribute("inkscape:connector-spacing") ) {
4883         gdouble priorValue = gtk_adjustment_get_value(adj);
4884         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4885         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4886             return;
4887         }
4888     } else if ( adj->value == defaultConnSpacing ) {
4889         return;
4890     }
4892     // in turn, prevent callbacks from responding
4893     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4895     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4896     SP_OBJECT(desktop->namedview)->updateRepr();
4898     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4899     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4900         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4901         NR::Matrix m = NR::identity();
4902         avoid_item_move(&m, item);
4903     }
4905     if (items) {
4906         g_slist_free(items);
4907     }
4909     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4910             _("Change connector spacing"));
4912     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4914     spinbutton_defocus(GTK_OBJECT(tbl));
4917 static void sp_connector_graph_layout(void)
4919     if (!SP_ACTIVE_DESKTOP) return;
4921     // hack for clones, see comment in align-and-distribute.cpp
4922     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4923     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4925     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4927     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4929     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4932 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4934     if ( gtk_toggle_action_get_active( act ) ) {
4935         prefs_set_string_attribute("tools.connector", "directedlayout",
4936                 "true");
4937     } else {
4938         prefs_set_string_attribute("tools.connector", "directedlayout",
4939                 "false");
4940     }
4943 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4945     if ( gtk_toggle_action_get_active( act ) ) {
4946         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4947                 "true");
4948     } else {
4949         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4950                 "false");
4951     }
4955 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4957     prefs_set_double_attribute("tools.connector", "length", adj->value);
4958     spinbutton_defocus(GTK_OBJECT(tbl));
4961 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4962                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
4963                                             bool /*is_interactive*/, gpointer data)
4965     GtkWidget *tbl = GTK_WIDGET(data);
4967     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4968         return;
4969     }
4970     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4971         return;
4972     }
4974     GtkAdjustment *adj = (GtkAdjustment*)
4975             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4976     gdouble spacing = defaultConnSpacing;
4977     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4979     gtk_adjustment_set_value(adj, spacing);
4983 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4984     NULL, /* child_added */
4985     NULL, /* child_removed */
4986     connector_tb_event_attr_changed,
4987     NULL, /* content_changed */
4988     NULL  /* order_changed */
4989 };
4992 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4994     {
4995         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4996                                           _("Avoid"),
4997                                           _("Make connectors avoid selected objects"),
4998                                           "connector_avoid",
4999                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5000         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5001         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5002     }
5004     {
5005         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5006                                           _("Ignore"),
5007                                           _("Make connectors ignore selected objects"),
5008                                           "connector_ignore",
5009                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5010         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5011         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5012     }
5014     EgeAdjustmentAction* eact = 0;
5016     // Spacing spinbox
5017     eact = create_adjustment_action( "ConnectorSpacingAction",
5018                                      _("Connector Spacing"), _("Spacing:"),
5019                                      _("The amount of space left around objects by auto-routing connectors"),
5020                                      "tools.connector", "spacing", defaultConnSpacing,
5021                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5022                                      0, 100, 1.0, 10.0,
5023                                      0, 0, 0,
5024                                      connector_spacing_changed, 1, 0 );
5025     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5027     // Graph (connector network) layout
5028     {
5029         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5030                                           _("Graph"),
5031                                           _("Nicely arrange selected connector network"),
5032                                           "graph_layout",
5033                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5034         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5035         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5036     }
5038     // Default connector length spinbox
5039     eact = create_adjustment_action( "ConnectorLengthAction",
5040                                      _("Connector Length"), _("Length:"),
5041                                      _("Ideal length for connectors when layout is applied"),
5042                                      "tools.connector", "length", 100,
5043                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5044                                      10, 1000, 10.0, 100.0,
5045                                      0, 0, 0,
5046                                      connector_length_changed, 1, 0 );
5047     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5050     // Directed edges toggle button
5051     {
5052         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5053                                                       _("Downwards"),
5054                                                       _("Make connectors with end-markers (arrows) point downwards"),
5055                                                       "directed_graph",
5056                                                       Inkscape::ICON_SIZE_DECORATION );
5057         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5059         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5060         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5061                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5063         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5064     }
5066     // Avoid overlaps toggle button
5067     {
5068         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5069                                                       _("Remove overlaps"),
5070                                                       _("Do not allow overlapping shapes"),
5071                                                       "remove_overlaps",
5072                                                       Inkscape::ICON_SIZE_DECORATION );
5073         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5075         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5076         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5077                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5079         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5080     }
5082     // Code to watch for changes to the connector-spacing attribute in
5083     // the XML.
5084     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5085     g_assert(repr != NULL);
5087     purge_repr_listener( holder, holder );
5089     if (repr) {
5090         g_object_set_data( holder, "repr", repr );
5091         Inkscape::GC::anchor(repr);
5092         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5093         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5094     }
5095 } // end of sp_connector_toolbox_prep()
5098 //#########################
5099 //##     Paintbucket     ##
5100 //#########################
5102 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5104     gint channels = ege_select_one_action_get_active( act );
5105     flood_channels_set_channels( channels );
5108 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5110     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5113 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5115     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5118 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5120     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5121     SPUnit const *unit = tracker->getActiveUnit();
5123     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5125     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5128 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5130     // FIXME: make defaults settable via Inkscape Options
5131     struct KeyValue {
5132         char const *key;
5133         double value;
5134     } const key_values[] = {
5135         {"threshold", 15},
5136         {"offset", 0.0}
5137     };
5139     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5140         KeyValue const &kv = key_values[i];
5141         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5142         if ( adj ) {
5143             gtk_adjustment_set_value(adj, kv.value);
5144         }
5145     }
5147     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5148     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5149     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5150     ege_select_one_action_set_active( autogap_action, 0 );
5153 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5155     EgeAdjustmentAction* eact = 0;
5157     {
5158         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5160         GList* items = 0;
5161         gint count = 0;
5162         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5163         {
5164             GtkTreeIter iter;
5165             gtk_list_store_append( model, &iter );
5166             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5167             count++;
5168         }
5169         g_list_free( items );
5170         items = 0;
5171         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5172         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5173         ege_select_one_action_set_appearance( act1, "compact" );
5174         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5175         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5176         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5177         g_object_set_data( holder, "channels_action", act1 );
5178     }
5180     // Spacing spinbox
5181     {
5182         eact = create_adjustment_action(
5183             "ThresholdAction",
5184             _("Fill Threshold"), _("Threshold:"),
5185             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5186             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5187             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5188             0, 0, 0,
5189             paintbucket_threshold_changed, 1, 0 );
5191         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5192     }
5194     // Create the units menu.
5195     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5196     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5197     g_object_set_data( holder, "tracker", tracker );
5198     {
5199         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5200         gtk_action_group_add_action( mainActions, act );
5201     }
5203     // Offset spinbox
5204     {
5205         eact = create_adjustment_action(
5206             "OffsetAction",
5207             _("Grow/shrink by"), _("Grow/shrink by:"),
5208             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5209             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5210             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5211             0, 0, 0,
5212             paintbucket_offset_changed, 1, 2);
5213         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5215         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5216     }
5218     /* Auto Gap */
5219     {
5220         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5222         GList* items = 0;
5223         gint count = 0;
5224         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5225         {
5226             GtkTreeIter iter;
5227             gtk_list_store_append( model, &iter );
5228             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5229             count++;
5230         }
5231         g_list_free( items );
5232         items = 0;
5233         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5234         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5235         ege_select_one_action_set_appearance( act2, "compact" );
5236         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5237         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5238         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5239         g_object_set_data( holder, "autogap_action", act2 );
5240     }
5242     /* Reset */
5243     {
5244         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5245                                           _("Defaults"),
5246                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5247                                           GTK_STOCK_CLEAR );
5248         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5249         gtk_action_group_add_action( mainActions, act );
5250         gtk_action_set_sensitive( act, TRUE );
5251     }
5255 /*
5256   Local Variables:
5257   mode:c++
5258   c-file-style:"stroustrup"
5259   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5260   indent-tabs-mode:nil
5261   fill-column:99
5262   End:
5263 */
5264 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :