Code

8cdf0a07fcee4869fb4a1fe4cddb09f19670a4cc
[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 <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "style.h"
78 #include "selection.h"
79 #include "selection-chemistry.h"
80 #include "document-private.h"
81 #include "desktop-style.h"
82 #include "../libnrtype/font-lister.h"
83 #include "../libnrtype/font-instance.h"
84 #include "../connection-pool.h"
85 #include "../prefs-utils.h"
86 #include "../inkscape-stock.h"
87 #include "icon.h"
88 #include "graphlayout/graphlayout.h"
90 #include "mod360.h"
92 #include "toolbox.h"
94 #include "flood-context.h"
96 #include "ink-action.h"
97 #include "ege-adjustment-action.h"
98 #include "ege-output-action.h"
99 #include "ege-select-one-action.h"
100 #include "helper/unit-tracker.h"
102 #include "svg/css-ostringstream.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
122 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
128 static struct {
129     gchar const *type_name;
130     gchar const *data_name;
131     sp_verb_t verb;
132     sp_verb_t doubleclick_verb;
133 } const tools[] = {
134     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
135     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
136     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
137     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
138     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
139     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
140     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
141     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
142     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
143     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
144     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
145     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
146     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
147     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
148     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
149     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
150     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
151     { NULL, NULL, 0, 0 }
152 };
154 static struct {
155     gchar const *type_name;
156     gchar const *data_name;
157     GtkWidget *(*create_func)(SPDesktop *desktop);
158     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
159     gchar const *ui_name;
160     gint swatch_verb_id;
161     gchar const *swatch_tool;
162     gchar const *swatch_tip;
163 } const aux_toolboxes[] = {
164     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
165       SP_VERB_INVALID, 0, 0},
166     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
167       SP_VERB_INVALID, 0, 0},
168     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
169       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
170     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
171       SP_VERB_INVALID, 0, 0},
172     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
173       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
174     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
175       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
176     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
177       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
178     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
179       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
180     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
181       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
182     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
183       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
184     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
185       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
186     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
187       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
188     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
189       SP_VERB_INVALID, 0, 0},
190     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
191       SP_VERB_INVALID, 0, 0},
192     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
193       SP_VERB_INVALID, 0, 0},
194     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
195       SP_VERB_INVALID, 0, 0},
196     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
197       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
198     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
199 };
202 static gchar const * ui_descr =
203         "<ui>"
204         "  <toolbar name='SelectToolbar'>"
205         "    <toolitem action='EditSelectAll' />"
206         "    <toolitem action='EditSelectAllInAllLayers' />"
207         "    <toolitem action='EditDeselect' />"
208         "    <separator />"
209         "    <toolitem action='ObjectRotate90CCW' />"
210         "    <toolitem action='ObjectRotate90' />"
211         "    <toolitem action='ObjectFlipHorizontally' />"
212         "    <toolitem action='ObjectFlipVertically' />"
213         "    <separator />"
214         "    <toolitem action='SelectionToBack' />"
215         "    <toolitem action='SelectionLower' />"
216         "    <toolitem action='SelectionRaise' />"
217         "    <toolitem action='SelectionToFront' />"
218         "    <separator />"
219         "    <toolitem action='XAction' />"
220         "    <toolitem action='YAction' />"
221         "    <toolitem action='WidthAction' />"
222         "    <toolitem action='LockAction' />"
223         "    <toolitem action='HeightAction' />"
224         "    <toolitem action='UnitsAction' />"
225         "    <separator />"
226         "    <toolitem action='transform_affect_label' />"
227         "    <toolitem action='transform_stroke' />"
228         "    <toolitem action='transform_corners' />"
229         "    <toolitem action='transform_gradient' />"
230         "    <toolitem action='transform_pattern' />"
231         "  </toolbar>"
233         "  <toolbar name='NodeToolbar'>"
234         "    <toolitem action='NodeInsertAction' />"
235         "    <toolitem action='NodeDeleteAction' />"
236         "    <separator />"
237         "    <toolitem action='NodeJoinAction' />"
238         "    <toolitem action='NodeJoinSegmentAction' />"
239         "    <toolitem action='NodeDeleteSegmentAction' />"
240         "    <toolitem action='NodeBreakAction' />"
241         "    <separator />"
242         "    <toolitem action='NodeCuspAction' />"
243         "    <toolitem action='NodeSmoothAction' />"
244         "    <toolitem action='NodeSymmetricAction' />"
245         "    <separator />"
246         "    <toolitem action='NodeLineAction' />"
247         "    <toolitem action='NodeCurveAction' />"
248         "    <separator />"
249         "    <toolitem action='ObjectToPath' />"
250         "    <toolitem action='StrokeToPath' />"
251         "    <separator />"
252         "    <toolitem action='NodesShowHandlesAction' />"
253         "    <separator />"
254         "    <toolitem action='EditNextLPEParameterAction' />"
255         "    <separator />"
256         "    <toolitem action='NodeXAction' />"
257         "    <toolitem action='NodeYAction' />"
258         "    <toolitem action='NodeUnitsAction' />"
259         "  </toolbar>"
261         "  <toolbar name='TweakToolbar'>"
262         "    <toolitem action='TweakWidthAction' />"
263         "    <separator />"
264         "    <toolitem action='TweakForceAction' />"
265         "    <toolitem action='TweakPressureAction' />"
266         "    <separator />"
267         "    <toolitem action='TweakModeAction' />"
268         "    <separator />"
269         "    <toolitem action='TweakFidelityAction' />"
270         "    <separator />"
271         "    <toolitem action='TweakChannelsLabel' />"
272         "    <toolitem action='TweakDoH' />"
273         "    <toolitem action='TweakDoS' />"
274         "    <toolitem action='TweakDoL' />"
275         "    <toolitem action='TweakDoO' />"
276         "  </toolbar>"
278         "  <toolbar name='ZoomToolbar'>"
279         "    <toolitem action='ZoomIn' />"
280         "    <toolitem action='ZoomOut' />"
281         "    <separator />"
282         "    <toolitem action='Zoom1:0' />"
283         "    <toolitem action='Zoom1:2' />"
284         "    <toolitem action='Zoom2:1' />"
285         "    <separator />"
286         "    <toolitem action='ZoomSelection' />"
287         "    <toolitem action='ZoomDrawing' />"
288         "    <toolitem action='ZoomPage' />"
289         "    <toolitem action='ZoomPageWidth' />"
290         "    <separator />"
291         "    <toolitem action='ZoomPrev' />"
292         "    <toolitem action='ZoomNext' />"
293         "  </toolbar>"
295         "  <toolbar name='StarToolbar'>"
296         "    <separator />"
297         "    <toolitem action='StarStateAction' />"
298         "    <separator />"
299         "    <toolitem action='FlatAction' />"
300         "    <separator />"
301         "    <toolitem action='MagnitudeAction' />"
302         "    <toolitem action='SpokeAction' />"
303         "    <toolitem action='RoundednessAction' />"
304         "    <toolitem action='RandomizationAction' />"
305         "    <separator />"
306         "    <toolitem action='StarResetAction' />"
307         "  </toolbar>"
309         "  <toolbar name='RectToolbar'>"
310         "    <toolitem action='RectStateAction' />"
311         "    <toolitem action='RectWidthAction' />"
312         "    <toolitem action='RectHeightAction' />"
313         "    <toolitem action='RadiusXAction' />"
314         "    <toolitem action='RadiusYAction' />"
315         "    <toolitem action='RectUnitsAction' />"
316         "    <separator />"
317         "    <toolitem action='RectResetAction' />"
318         "  </toolbar>"
320         "  <toolbar name='3DBoxToolbar'>"
321         "    <toolitem action='3DBoxAngleXAction' />"
322         "    <toolitem action='3DBoxVPXStateAction' />"
323         "    <separator />"
324         "    <toolitem action='3DBoxAngleYAction' />"
325         "    <toolitem action='3DBoxVPYStateAction' />"
326         "    <separator />"
327         "    <toolitem action='3DBoxAngleZAction' />"
328         "    <toolitem action='3DBoxVPZStateAction' />"
329         "  </toolbar>"
331         "  <toolbar name='SpiralToolbar'>"
332         "    <toolitem action='SpiralStateAction' />"
333         "    <toolitem action='SpiralRevolutionAction' />"
334         "    <toolitem action='SpiralExpansionAction' />"
335         "    <toolitem action='SpiralT0Action' />"
336         "    <separator />"
337         "    <toolitem action='SpiralResetAction' />"
338         "  </toolbar>"
340         "  <toolbar name='PenToolbar'>"
341         "  </toolbar>"
343         "  <toolbar name='PencilToolbar'>"
344         "  </toolbar>"
346         "  <toolbar name='CalligraphyToolbar'>"
347         "    <separator />"
348         "    <toolitem action='CalligraphyWidthAction' />"
349         "    <toolitem action='PressureAction' />"
350         "    <toolitem action='TraceAction' />"
351         "    <toolitem action='ThinningAction' />"
352         "    <separator />"
353         "    <toolitem action='AngleAction' />"
354         "    <toolitem action='TiltAction' />"
355         "    <toolitem action='FixationAction' />"
356         "    <separator />"
357         "    <toolitem action='CapRoundingAction' />"
358         "    <separator />"
359         "    <toolitem action='TremorAction' />"
360         "    <toolitem action='WiggleAction' />"
361         "    <toolitem action='MassAction' />"
362         "    <separator />"
363         "    <toolitem action='CalligraphyResetAction' />"
364         "  </toolbar>"
366         "  <toolbar name='ArcToolbar'>"
367         "    <toolitem action='ArcStateAction' />"
368         "    <separator />"
369         "    <toolitem action='ArcStartAction' />"
370         "    <toolitem action='ArcEndAction' />"
371         "    <separator />"
372         "    <toolitem action='ArcOpenAction' />"
373         "    <separator />"
374         "    <toolitem action='ArcResetAction' />"
375         "    <separator />"
376         "  </toolbar>"
378         "  <toolbar name='PaintbucketToolbar'>"
379         "    <toolitem action='ChannelsAction' />"
380         "    <separator />"
381         "    <toolitem action='ThresholdAction' />"
382         "    <separator />"
383         "    <toolitem action='OffsetAction' />"
384         "    <toolitem action='PaintbucketUnitsAction' />"
385         "    <separator />"
386         "    <toolitem action='AutoGapAction' />"
387         "    <separator />"
388         "    <toolitem action='PaintbucketResetAction' />"
389         "  </toolbar>"
391         "  <toolbar name='DropperToolbar'>"
392         "    <toolitem action='DropperPickAlphaAction' />"
393         "    <toolitem action='DropperSetAlphaAction' />"
394         "  </toolbar>"
396         "  <toolbar name='ConnectorToolbar'>"
397         "    <toolitem action='ConnectorAvoidAction' />"
398         "    <toolitem action='ConnectorIgnoreAction' />"
399         "    <toolitem action='ConnectorSpacingAction' />"
400         "    <toolitem action='ConnectorGraphAction' />"
401         "    <toolitem action='ConnectorLengthAction' />"
402         "    <toolitem action='ConnectorDirectedAction' />"
403         "    <toolitem action='ConnectorOverlapAction' />"
404         "  </toolbar>"
406         "</ui>"
409 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
411 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
413 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
414 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
416 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
417 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
419 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
420 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
422 /* Global text entry widgets necessary for update */
423 /* GtkWidget *dropper_rgb_entry,
424           *dropper_opacity_entry ; */
425 // should be made a private member once this is converted to class
427 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
428     connection->disconnect();
429     delete connection;
432 static void purge_repr_listener( GObject* obj, GObject* tbl )
434     (void)obj;
435     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
436     if (oldrepr) { // remove old listener
437         sp_repr_remove_listener_by_data(oldrepr, tbl);
438         Inkscape::GC::release(oldrepr);
439         oldrepr = 0;
440         g_object_set_data( tbl, "repr", NULL );
441     }
444 GtkWidget *
445 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
446                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
447                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
449     SPAction *action = verb->get_action(view);
450     if (!action) return NULL;
452     SPAction *doubleclick_action;
453     if (doubleclick_verb)
454         doubleclick_action = doubleclick_verb->get_action(view);
455     else
456         doubleclick_action = NULL;
458     /* fixme: Handle sensitive/unsensitive */
459     /* fixme: Implement sp_button_new_from_action */
460     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
461     gtk_widget_show(b);
462     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
464     return b;
467 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
468                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
470     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
473 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
474                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
476     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
480 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
482     SPAction* targetAction = SP_ACTION(user_data);
483     if ( targetAction ) {
484         sp_action_perform( targetAction, NULL );
485     }
488 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
490     if ( data ) {
491         GtkAction* act = GTK_ACTION(data);
492         gtk_action_set_sensitive( act, sensitive );
493     }
496 static SPActionEventVector action_event_vector = {
497     {NULL},
498     NULL,
499     NULL,
500     sp_action_action_set_sensitive,
501     NULL,
502     NULL
503 };
505 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
507     GtkAction* act = 0;
509     SPAction* targetAction = verb->get_action(view);
510     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
511     act = GTK_ACTION(inky);
512     gtk_action_set_sensitive( act, targetAction->sensitive );
514     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
516     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
517     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
519     return act;
522 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
524     Inkscape::UI::View::View *view = desktop;
525     gint verbsToUse[] = {
526         // disabled until we have icons for them:
527         //find
528         //SP_VERB_EDIT_TILE,
529         //SP_VERB_EDIT_UNTILE,
530         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
531         SP_VERB_DIALOG_DISPLAY,
532         SP_VERB_DIALOG_FILL_STROKE,
533         SP_VERB_DIALOG_NAMEDVIEW,
534         SP_VERB_DIALOG_TEXT,
535         SP_VERB_DIALOG_XML_EDITOR,
536         SP_VERB_EDIT_CLONE,
537         SP_VERB_EDIT_COPY,
538         SP_VERB_EDIT_CUT,
539         SP_VERB_EDIT_DUPLICATE,
540         SP_VERB_EDIT_PASTE,
541         SP_VERB_EDIT_REDO,
542         SP_VERB_EDIT_UNDO,
543         SP_VERB_EDIT_UNLINK_CLONE,
544         SP_VERB_FILE_EXPORT,
545         SP_VERB_FILE_IMPORT,
546         SP_VERB_FILE_NEW,
547         SP_VERB_FILE_OPEN,
548         SP_VERB_FILE_PRINT,
549         SP_VERB_FILE_SAVE,
550         SP_VERB_OBJECT_TO_CURVE,
551         SP_VERB_SELECTION_GROUP,
552         SP_VERB_SELECTION_OUTLINE,
553         SP_VERB_SELECTION_UNGROUP,
554         SP_VERB_ZOOM_1_1,
555         SP_VERB_ZOOM_1_2,
556         SP_VERB_ZOOM_2_1,
557         SP_VERB_ZOOM_DRAWING,
558         SP_VERB_ZOOM_IN,
559         SP_VERB_ZOOM_NEXT,
560         SP_VERB_ZOOM_OUT,
561         SP_VERB_ZOOM_PAGE,
562         SP_VERB_ZOOM_PAGE_WIDTH,
563         SP_VERB_ZOOM_PREV,
564         SP_VERB_ZOOM_SELECTION,
565     };
567     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
568     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
570     static std::map<SPDesktop*, GtkActionGroup*> groups;
571     GtkActionGroup* mainActions = 0;
572     if ( groups.find(desktop) != groups.end() ) {
573         mainActions = groups[desktop];
574     }
576     if ( !mainActions ) {
577         mainActions = gtk_action_group_new("main");
578         groups[desktop] = mainActions;
579     }
581     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
582         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
583         if ( verb ) {
584             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
585                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
586                 gtk_action_group_add_action( mainActions, act );
587             }
588         }
589     }
591     return mainActions;
595 GtkWidget *
596 sp_tool_toolbox_new()
598     GtkTooltips *tt = gtk_tooltips_new();
599     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
601     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
602     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
604     gtk_widget_set_sensitive(tb, FALSE);
606     GtkWidget *hb = gtk_handle_box_new();
607     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
608     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
609     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
611     gtk_container_add(GTK_CONTAINER(hb), tb);
612     gtk_widget_show(GTK_WIDGET(tb));
614     sigc::connection* conn = new sigc::connection;
615     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
617     return hb;
620 static void
621 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
623     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
624     gtk_widget_queue_resize(child);
627 static void
628 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
630     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
631     gtk_widget_queue_resize(child);
634 GtkWidget *
635 sp_aux_toolbox_new()
637     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
639     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
641     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
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     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
673     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
674     gtk_widget_set_sensitive(tb, FALSE);
676     GtkWidget *hb = gtk_handle_box_new();
677     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
678     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
679     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
681     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
682     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
684     gtk_container_add(GTK_CONTAINER(hb), tb);
685     gtk_widget_show(GTK_WIDGET(tb));
687     sigc::connection* conn = new sigc::connection;
688     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
690     return hb;
693 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
694                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
695                                                        gchar const *path, gchar const *data, gdouble def,
696                                                        GtkWidget *focusTarget,
697                                                        GtkWidget *us,
698                                                        GObject *dataKludge,
699                                                        gboolean altx, gchar const *altx_mark,
700                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
701                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
702                                                        void (*callback)(GtkAdjustment *, GObject *),
703                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
705     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
706                                                              lower, upper, step, page, page ) );
707     if (us) {
708         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
709     }
711     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
713     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
714     if ( shortLabel ) {
715         g_object_set( act, "short_label", shortLabel, NULL );
716     }
718     if ( (descrCount > 0) && descrLabels && descrValues ) {
719         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
720     }
722     if ( focusTarget ) {
723         ege_adjustment_action_set_focuswidget( act, focusTarget );
724     }
726     if ( altx && altx_mark ) {
727         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
728     }
730     if ( dataKludge ) {
731         g_object_set_data( dataKludge, data, adj );
732     }
734     // Using a cast just to make sure we pass in the right kind of function pointer
735     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
737     return act;
741 //####################################
742 //# node editing callbacks
743 //####################################
745 /**
746  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
747  */
748 static ShapeEditor *get_current_shape_editor()
750     if (!SP_ACTIVE_DESKTOP) {
751         return NULL;
752     }
754     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
756     if (!SP_IS_NODE_CONTEXT(event_context)) {
757         return NULL;
758     }
760     return SP_NODE_CONTEXT(event_context)->shape_editor;
764 void
765 sp_node_path_edit_add(void)
767     ShapeEditor *shape_editor = get_current_shape_editor();
768     if (shape_editor) shape_editor->add_node();
771 void
772 sp_node_path_edit_delete(void)
774     ShapeEditor *shape_editor = get_current_shape_editor();
775     if (shape_editor) shape_editor->delete_nodes();
778 void
779 sp_node_path_edit_delete_segment(void)
781     ShapeEditor *shape_editor = get_current_shape_editor();
782     if (shape_editor) shape_editor->delete_segment();
785 void
786 sp_node_path_edit_break(void)
788     ShapeEditor *shape_editor = get_current_shape_editor();
789     if (shape_editor) shape_editor->break_at_nodes();
792 void
793 sp_node_path_edit_join(void)
795     ShapeEditor *shape_editor = get_current_shape_editor();
796     if (shape_editor) shape_editor->join_nodes();
799 void
800 sp_node_path_edit_join_segment(void)
802     ShapeEditor *shape_editor = get_current_shape_editor();
803     if (shape_editor) shape_editor->join_segments();
806 void
807 sp_node_path_edit_toline(void)
809     ShapeEditor *shape_editor = get_current_shape_editor();
810     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
813 void
814 sp_node_path_edit_tocurve(void)
816     ShapeEditor *shape_editor = get_current_shape_editor();
817     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
820 void
821 sp_node_path_edit_cusp(void)
823     ShapeEditor *shape_editor = get_current_shape_editor();
824     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
827 void
828 sp_node_path_edit_smooth(void)
830     ShapeEditor *shape_editor = get_current_shape_editor();
831     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
834 void
835 sp_node_path_edit_symmetrical(void)
837     ShapeEditor *shape_editor = get_current_shape_editor();
838     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
841 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
842     bool show = gtk_toggle_action_get_active( act );
843     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
844     ShapeEditor *shape_editor = get_current_shape_editor();
845     if (shape_editor) shape_editor->show_handles(show);
848 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
849     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
852 /* is called when the node selection is modified */
853 static void
854 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
856     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
857     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
858     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
859     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
861     // quit if run by the attr_changed listener
862     if (g_object_get_data( tbl, "freeze" )) {
863         return;
864     }
866     // in turn, prevent listener from responding
867     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
869     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
870     SPUnit const *unit = tracker->getActiveUnit();
872     ShapeEditor *shape_editor = get_current_shape_editor();
873     if (shape_editor && shape_editor->has_nodepath()) {
874         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
875         int n_selected = 0;
876         if (nodepath) {
877             n_selected = nodepath->numSelected();
878         }
880         if (n_selected == 0) {
881             gtk_action_set_sensitive(xact, FALSE);
882             gtk_action_set_sensitive(yact, FALSE);
883         } else {
884             gtk_action_set_sensitive(xact, TRUE);
885             gtk_action_set_sensitive(yact, TRUE);
886             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
887             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
889             if (n_selected == 1) {
890                 NR::Point sel_node = nodepath->singleSelectedCoords();
891                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
892                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
893                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
894                 }
895             } else {
896                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
897                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
898                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
899                     /* Note: Currently x and y will always have a value, even if the coordinates of the
900                        selected nodes don't coincide (in this case we use the coordinates of the center
901                        of the bounding box). So the entries are never set to zero. */
902                     // FIXME: Maybe we should clear the entry if several nodes are selected
903                     //        instead of providing a kind of average value
904                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
905                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
906                 }
907             }
908         }
909     } else {
910         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
911         gtk_action_set_sensitive(xact, FALSE);
912         gtk_action_set_sensitive(yact, FALSE);
913     }
915     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
918 static void
919 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
921     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
923     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
924     SPUnit const *unit = tracker->getActiveUnit();
926     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
927         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
928     }
930     // quit if run by the attr_changed listener
931     if (g_object_get_data( tbl, "freeze" )) {
932         return;
933     }
935     // in turn, prevent listener from responding
936     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
938     ShapeEditor *shape_editor = get_current_shape_editor();
939     if (shape_editor && shape_editor->has_nodepath()) {
940         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
941         if (!strcmp(value_name, "x")) {
942             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
943         }
944         if (!strcmp(value_name, "y")) {
945             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
946         }
947     }
949     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
952 static void
953 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
955     sp_node_path_value_changed(adj, tbl, "x");
958 static void
959 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
961     sp_node_path_value_changed(adj, tbl, "y");
964 //################################
965 //##    Node Editing Toolbox    ##
966 //################################
968 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
970     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
971     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
972     g_object_set_data( holder, "tracker", tracker );
974     {
975         InkAction* inky = ink_action_new( "NodeInsertAction",
976                                           _("Insert node"),
977                                           _("Insert new nodes into selected segments"),
978                                           "node_insert",
979                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
980         g_object_set( inky, "short_label", _("Insert"), NULL );
981         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
982         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
983     }
985     {
986         InkAction* inky = ink_action_new( "NodeDeleteAction",
987                                           _("Delete node"),
988                                           _("Delete selected nodes"),
989                                           "node_delete",
990                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
991         g_object_set( inky, "short_label", _("Delete"), NULL );
992         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
993         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
994     }
996     {
997         InkAction* inky = ink_action_new( "NodeJoinAction",
998                                           _("Join endnodes"),
999                                           _("Join selected endnodes"),
1000                                           "node_join",
1001                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1002         g_object_set( inky, "short_label", _("Join"), NULL );
1003         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1004         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1005     }
1007     {
1008         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1009                                           _("Join Segment"),
1010                                           _("Join selected endnodes with a new segment"),
1011                                           "node_join_segment",
1012                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1013         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1014         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1015     }
1017     {
1018         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1019                                           _("Delete Segment"),
1020                                           _("Split path between two non-endpoint nodes"),
1021                                           "node_delete_segment",
1022                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1023         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1024         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1025     }
1027     {
1028         InkAction* inky = ink_action_new( "NodeBreakAction",
1029                                           _("Node Break"),
1030                                           _("Break path at selected nodes"),
1031                                           "node_break",
1032                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1033         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1034         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1035     }
1037     {
1038         InkAction* inky = ink_action_new( "NodeCuspAction",
1039                                           _("Node Cusp"),
1040                                           _("Make selected nodes corner"),
1041                                           "node_cusp",
1042                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1043         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1044         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1045     }
1047     {
1048         InkAction* inky = ink_action_new( "NodeSmoothAction",
1049                                           _("Node Smooth"),
1050                                           _("Make selected nodes smooth"),
1051                                           "node_smooth",
1052                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1053         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1054         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1055     }
1057     {
1058         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1059                                           _("Node Symmetric"),
1060                                           _("Make selected nodes symmetric"),
1061                                           "node_symmetric",
1062                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1063         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1064         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1065     }
1067     {
1068         InkAction* inky = ink_action_new( "NodeLineAction",
1069                                           _("Node Line"),
1070                                           _("Make selected segments lines"),
1071                                           "node_line",
1072                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1073         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1074         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1075     }
1077     {
1078         InkAction* inky = ink_action_new( "NodeCurveAction",
1079                                           _("Node Curve"),
1080                                           _("Make selected segments curves"),
1081                                           "node_curve",
1082                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1083         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1084         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1085     }
1087     {
1088         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1089                                                       _("Show Handles"),
1090                                                       _("Show the Bezier handles of selected nodes"),
1091                                                       "nodes_show_handles",
1092                                                       Inkscape::ICON_SIZE_DECORATION );
1093         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1094         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1095         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1096     }
1098     {
1099         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1100                                           _("Next Path Effect Parameter"),
1101                                           _("Show next Path Effect parameter for editing"),
1102                                           "edit_next_parameter",
1103                                           Inkscape::ICON_SIZE_DECORATION );
1104         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1105         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1106     }
1108     /* X coord of selected node(s) */
1109     {
1110         EgeAdjustmentAction* eact = 0;
1111         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1112         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1113         eact = create_adjustment_action( "NodeXAction",
1114                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1115                                          "tools.nodes", "Xcoord", 0,
1116                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1117                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1118                                          labels, values, G_N_ELEMENTS(labels),
1119                                          sp_node_path_x_value_changed );
1120         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1121         g_object_set_data( holder, "nodes_x_action", eact );
1122         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1123         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1124     }
1126     /* Y coord of selected node(s) */
1127     {
1128         EgeAdjustmentAction* eact = 0;
1129         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1130         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1131         eact = create_adjustment_action( "NodeYAction",
1132                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1133                                          "tools.nodes", "Ycoord", 0,
1134                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1135                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1136                                          labels, values, G_N_ELEMENTS(labels),
1137                                          sp_node_path_y_value_changed );
1138         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1139         g_object_set_data( holder, "nodes_y_action", eact );
1140         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1141         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1142     }
1144     // add the units menu
1145     {
1146         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1147         gtk_action_group_add_action( mainActions, act );
1148     }
1150     sigc::connection *connection = new sigc::connection (
1151         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1152         );
1154     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1155     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1156 } // end of sp_node_toolbox_prep()
1159 //########################
1160 //##    Zoom Toolbox    ##
1161 //########################
1163 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1165     // no custom GtkAction setup needed
1166 } // end of sp_zoom_toolbox_prep()
1168 void
1169 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1171     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")));
1175 void
1176 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1178     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")));
1181 void
1182 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1184     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")));
1187 static void
1188 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1190     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1191     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1193     if (old_desktop) {
1194         GList *children, *iter;
1196         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1197         for ( iter = children ; iter ; iter = iter->next ) {
1198             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1199         }
1200         g_list_free(children);
1201     }
1203     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1205     if (desktop) {
1206         gtk_widget_set_sensitive(toolbox, TRUE);
1207         setup_func(toolbox, desktop);
1208         update_func(desktop, desktop->event_context, toolbox);
1209         *conn = desktop->connectEventContextChanged
1210             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1211     } else {
1212         gtk_widget_set_sensitive(toolbox, FALSE);
1213     }
1215 } // end of toolbox_set_desktop()
1218 static void
1219 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1221     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1222     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1223     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1224         // "toolbox.tools" was not set. Fallback to older value
1225         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1227         // Copy the setting forwards
1228         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1229     }
1230     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1232     for (int i = 0 ; tools[i].type_name ; i++ ) {
1233         GtkWidget *button =
1234             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1235                                                               SP_BUTTON_TYPE_TOGGLE,
1236                                                               Inkscape::Verb::get(tools[i].verb),
1237                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1238                                                               desktop,
1239                                                               tooltips );
1241         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1242                            (gpointer)button );
1243     }
1247 static void
1248 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1250     gchar const *const tname = ( eventcontext
1251                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1252                                  : NULL );
1253     for (int i = 0 ; tools[i].type_name ; i++ ) {
1254         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1255         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1256     }
1259 static void
1260 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1262     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1263     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1264     GtkUIManager* mgr = gtk_ui_manager_new();
1265     GError* errVal = 0;
1266     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1267     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1269     std::map<std::string, GtkWidget*> dataHolders;
1271     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1272         if ( aux_toolboxes[i].prep_func ) {
1273             // converted to GtkActions and UIManager
1275             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1276             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1277             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1278             dataHolders[aux_toolboxes[i].type_name] = kludge;
1279             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1280         } else {
1282             GtkWidget *sub_toolbox = 0;
1283             if (aux_toolboxes[i].create_func == NULL)
1284                 sub_toolbox = sp_empty_toolbox_new(desktop);
1285             else {
1286                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1287             }
1289             gtk_size_group_add_widget( grouper, sub_toolbox );
1291             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1292             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1294         }
1295     }
1297     // Second pass to create toolbars *after* all GtkActions are created
1298     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1299         if ( aux_toolboxes[i].prep_func ) {
1300             // converted to GtkActions and UIManager
1302             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1304             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1305             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1307             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1308             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1309             g_free( tmp );
1310             tmp = 0;
1312             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1313             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1314             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1315                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1316             }
1317             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1320             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1322             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1323                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1324                 swatch->setDesktop( desktop );
1325                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1326                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1327                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1328                 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 );
1329             }
1331             gtk_widget_show_all( holder );
1332             sp_set_font_size_smaller( holder );
1334             gtk_size_group_add_widget( grouper, holder );
1336             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1337             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1338         }
1339     }
1341     g_object_unref( G_OBJECT(grouper) );
1344 static void
1345 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1347     gchar const *tname = ( eventcontext
1348                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1349                            : NULL );
1350     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1351         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1352         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1353             gtk_widget_show_all(sub_toolbox);
1354             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1355         } else {
1356             gtk_widget_hide(sub_toolbox);
1357         }
1358     }
1361 static void
1362 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1364     gchar const * descr =
1365         "<ui>"
1366         "  <toolbar name='CommandsToolbar'>"
1367         "    <toolitem action='FileNew' />"
1368         "    <toolitem action='FileOpen' />"
1369         "    <toolitem action='FileSave' />"
1370         "    <toolitem action='FilePrint' />"
1371         "    <separator />"
1372         "    <toolitem action='FileImport' />"
1373         "    <toolitem action='FileExport' />"
1374         "    <separator />"
1375         "    <toolitem action='EditUndo' />"
1376         "    <toolitem action='EditRedo' />"
1377         "    <separator />"
1378         "    <toolitem action='EditCopy' />"
1379         "    <toolitem action='EditCut' />"
1380         "    <toolitem action='EditPaste' />"
1381         "    <separator />"
1382         "    <toolitem action='ZoomSelection' />"
1383         "    <toolitem action='ZoomDrawing' />"
1384         "    <toolitem action='ZoomPage' />"
1385         "    <separator />"
1386         "    <toolitem action='EditDuplicate' />"
1387         "    <toolitem action='EditClone' />"
1388         "    <toolitem action='EditUnlinkClone' />"
1389         "    <separator />"
1390         "    <toolitem action='SelectionGroup' />"
1391         "    <toolitem action='SelectionUnGroup' />"
1392         "    <separator />"
1393         "    <toolitem action='DialogFillStroke' />"
1394         "    <toolitem action='DialogText' />"
1395         "    <toolitem action='DialogXMLEditor' />"
1396         "    <toolitem action='DialogAlignDistribute' />"
1397         "    <separator />"
1398         "    <toolitem action='DialogPreferences' />"
1399         "    <toolitem action='DialogDocumentProperties' />"
1400         "  </toolbar>"
1401         "</ui>";
1402     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1405     GtkUIManager* mgr = gtk_ui_manager_new();
1406     GError* errVal = 0;
1408     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1409     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1411     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1412     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1413         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1414     }
1415     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1416     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1417     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1420     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1423 static void
1424 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1428 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1430     gtk_widget_show(toolbox_toplevel);
1431     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1433     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1434     if (!shown_toolbox) {
1435         return;
1436     }
1437     gtk_widget_show(toolbox);
1439     gtk_widget_show_all(shown_toolbox);
1442 void
1443 aux_toolbox_space(GtkWidget *tb, gint space)
1445     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1448 static GtkWidget *
1449 sp_empty_toolbox_new(SPDesktop *desktop)
1451     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1452     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1453     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1455     gtk_widget_show_all(tbl);
1456     sp_set_font_size_smaller (tbl);
1458     return tbl;
1461 // helper UI functions
1463 GtkWidget *
1464 sp_tb_spinbutton(
1465     gchar *label, gchar const *tooltip,
1466     gchar const *path, gchar const *data, gdouble def,
1467     GtkWidget *us,
1468     GtkWidget *tbl,
1469     gboolean altx, gchar const *altx_mark,
1470     gdouble lower, gdouble upper, gdouble step, gdouble page,
1471     void (*callback)(GtkAdjustment *, GtkWidget *),
1472     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1474     GtkTooltips *tt = gtk_tooltips_new();
1476     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1478     GtkWidget *l = gtk_label_new(label);
1479     gtk_widget_show(l);
1480     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1481     gtk_container_add(GTK_CONTAINER(hb), l);
1483     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1484                                       lower, upper, step, page, page);
1485     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1486     if (us)
1487         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1489     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1490     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1491     if (altx)
1492         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1493     gtk_widget_set_size_request(sb,
1494                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1495                                 AUX_SPINBUTTON_HEIGHT);
1496     gtk_widget_show(sb);
1497     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1498     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1499     gtk_container_add(GTK_CONTAINER(hb), sb);
1500     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1502     return hb;
1505 #define MODE_LABEL_WIDTH 70
1507 //########################
1508 //##       Star         ##
1509 //########################
1511 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1513     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1515     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1516         // do not remember prefs if this call is initiated by an undo change, because undoing object
1517         // creation sets bogus values to its attributes before it is deleted
1518         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1519     }
1521     // quit if run by the attr_changed listener
1522     if (g_object_get_data( dataKludge, "freeze" )) {
1523         return;
1524     }
1526     // in turn, prevent listener from responding
1527     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1529     bool modmade = false;
1531     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1532     GSList const *items = selection->itemList();
1533     for (; items != NULL; items = items->next) {
1534         if (SP_IS_STAR((SPItem *) items->data)) {
1535             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1536             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1537             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1538                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1539                                     + M_PI / (gint)adj->value));
1540             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1541             modmade = true;
1542         }
1543     }
1544     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1545                                    _("Star: Change number of corners"));
1547     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1550 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1552     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1554     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1555         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1556     }
1558     // quit if run by the attr_changed listener
1559     if (g_object_get_data( dataKludge, "freeze" )) {
1560         return;
1561     }
1563     // in turn, prevent listener from responding
1564     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1566     bool modmade = false;
1567     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1568     GSList const *items = selection->itemList();
1569     for (; items != NULL; items = items->next) {
1570         if (SP_IS_STAR((SPItem *) items->data)) {
1571             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1573             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1574             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1575             if (r2 < r1) {
1576                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1577             } else {
1578                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1579             }
1581             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1582             modmade = true;
1583         }
1584     }
1586     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1587                                    _("Star: Change spoke ratio"));
1589     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1592 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1594     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1595     bool flat = ege_select_one_action_get_active( act ) == 0;
1597     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1598         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1599                                     flat ? "true" : "false" );
1600     }
1602     // quit if run by the attr_changed listener
1603     if (g_object_get_data( dataKludge, "freeze" )) {
1604         return;
1605     }
1607     // in turn, prevent listener from responding
1608     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1610     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1611     GSList const *items = selection->itemList();
1612     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1613     bool modmade = false;
1615     if ( prop_action ) {
1616         gtk_action_set_sensitive( prop_action, !flat );
1617     }
1619     for (; items != NULL; items = items->next) {
1620         if (SP_IS_STAR((SPItem *) items->data)) {
1621             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1622             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1623             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1624             modmade = true;
1625         }
1626     }
1628     if (modmade) {
1629         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1630                          flat ? _("Make polygon") : _("Make star"));
1631     }
1633     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1636 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1638     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1640     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1641         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1642     }
1644     // quit if run by the attr_changed listener
1645     if (g_object_get_data( dataKludge, "freeze" )) {
1646         return;
1647     }
1649     // in turn, prevent listener from responding
1650     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1652     bool modmade = false;
1654     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1655     GSList const *items = selection->itemList();
1656     for (; items != NULL; items = items->next) {
1657         if (SP_IS_STAR((SPItem *) items->data)) {
1658             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1659             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1660             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1661             modmade = true;
1662         }
1663     }
1664     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1665                                    _("Star: Change rounding"));
1667     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1670 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1672     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1674     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1675         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1676     }
1678     // quit if run by the attr_changed listener
1679     if (g_object_get_data( dataKludge, "freeze" )) {
1680         return;
1681     }
1683     // in turn, prevent listener from responding
1684     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1686     bool modmade = false;
1688     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1689     GSList const *items = selection->itemList();
1690     for (; items != NULL; items = items->next) {
1691         if (SP_IS_STAR((SPItem *) items->data)) {
1692             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1693             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1694             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1695             modmade = true;
1696         }
1697     }
1698     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1699                                    _("Star: Change randomization"));
1701     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1705 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1706                                        gchar const */*old_value*/, gchar const */*new_value*/,
1707                                        bool /*is_interactive*/, gpointer data)
1709     GtkWidget *tbl = GTK_WIDGET(data);
1711     // quit if run by the _changed callbacks
1712     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1713         return;
1714     }
1716     // in turn, prevent callbacks from responding
1717     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1719     GtkAdjustment *adj = 0;
1721     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1722     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1724     if (!strcmp(name, "inkscape:randomized")) {
1725         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1726         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1727     } else if (!strcmp(name, "inkscape:rounded")) {
1728         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1729         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1730     } else if (!strcmp(name, "inkscape:flatsided")) {
1731         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1732         char const *flatsides = repr->attribute("inkscape:flatsided");
1733         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1734         if ( flatsides && !strcmp(flatsides,"false") ) {
1735             ege_select_one_action_set_active( flat_action, 1 );
1736             gtk_action_set_sensitive( prop_action, TRUE );
1737         } else {
1738             ege_select_one_action_set_active( flat_action, 0 );
1739             gtk_action_set_sensitive( prop_action, FALSE );
1740         }
1741     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1742         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1743         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1744         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1745         if (r2 < r1) {
1746             gtk_adjustment_set_value(adj, r2/r1);
1747         } else {
1748             gtk_adjustment_set_value(adj, r1/r2);
1749         }
1750     } else if (!strcmp(name, "sodipodi:sides")) {
1751         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1752         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1753     }
1755     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1759 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1761     NULL, /* child_added */
1762     NULL, /* child_removed */
1763     star_tb_event_attr_changed,
1764     NULL, /* content_changed */
1765     NULL  /* order_changed */
1766 };
1769 /**
1770  *  \param selection Should not be NULL.
1771  */
1772 static void
1773 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1775     int n_selected = 0;
1776     Inkscape::XML::Node *repr = NULL;
1778     purge_repr_listener( tbl, tbl );
1780     for (GSList const *items = selection->itemList();
1781          items != NULL;
1782          items = items->next)
1783     {
1784         if (SP_IS_STAR((SPItem *) items->data)) {
1785             n_selected++;
1786             repr = SP_OBJECT_REPR((SPItem *) items->data);
1787         }
1788     }
1790     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1792     if (n_selected == 0) {
1793         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1794     } else if (n_selected == 1) {
1795         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1797         if (repr) {
1798             g_object_set_data( tbl, "repr", repr );
1799             Inkscape::GC::anchor(repr);
1800             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1801             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1802         }
1803     } else {
1804         // FIXME: implement averaging of all parameters for multiple selected stars
1805         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1806         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1807     }
1811 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1813     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1814     // callbacks to lump all the changes for all selected objects in one undo step
1816     GtkAdjustment *adj = 0;
1818     // fixme: make settable in prefs!
1819     gint mag = 5;
1820     gdouble prop = 0.5;
1821     gboolean flat = FALSE;
1822     gdouble randomized = 0;
1823     gdouble rounded = 0;
1825     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1826     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1828     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1829     gtk_action_set_sensitive( sb2, !flat );
1831     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1832     gtk_adjustment_set_value(adj, mag);
1833     gtk_adjustment_value_changed(adj);
1835     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1836     gtk_adjustment_set_value(adj, prop);
1837     gtk_adjustment_value_changed(adj);
1839     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1840     gtk_adjustment_set_value(adj, rounded);
1841     gtk_adjustment_value_changed(adj);
1843     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1844     gtk_adjustment_set_value(adj, randomized);
1845     gtk_adjustment_value_changed(adj);
1849 void
1850 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1852     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1853     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1854     GtkWidget *l = gtk_label_new(NULL);
1855     gtk_label_set_markup(GTK_LABEL(l), title);
1856     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1857     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1858     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1862 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1864     {
1865         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1866         ege_output_action_set_use_markup( act, TRUE );
1867         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1868         g_object_set_data( holder, "mode_action", act );
1869     }
1871     {
1872         EgeAdjustmentAction* eact = 0;
1873         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1874         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1876         /* Flatsided checkbox */
1877         {
1878             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1880             GtkTreeIter iter;
1881             gtk_list_store_append( model, &iter );
1882             gtk_list_store_set( model, &iter,
1883                                 0, _("Polygon"),
1884                                 1, _("Regular polygon (with one handle) instead of a star"),
1885                                 2, "star_flat",
1886                                 -1 );
1888             gtk_list_store_append( model, &iter );
1889             gtk_list_store_set( model, &iter,
1890                                 0, _("Star"),
1891                                 1, _("Star instead of a regular polygon (with one handle)"),
1892                                 2, "star_angled",
1893                                 -1 );
1895             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1896             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1897             g_object_set_data( holder, "flat_action", act );
1899             ege_select_one_action_set_appearance( act, "full" );
1900             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1901             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1902             ege_select_one_action_set_icon_column( act, 2 );
1903             ege_select_one_action_set_tooltip_column( act, 1  );
1905             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1906             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1907         }
1909         /* Magnitude */
1910         {
1911         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1912         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1913         eact = create_adjustment_action( "MagnitudeAction",
1914                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1915                                          "tools.shapes.star", "magnitude", 3,
1916                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1917                                          3, 1024, 1, 5,
1918                                          labels, values, G_N_ELEMENTS(labels),
1919                                          sp_stb_magnitude_value_changed,
1920                                          1.0, 0 );
1921         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1922         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1923         }
1925         /* Spoke ratio */
1926         {
1927         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1928         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1929         eact = create_adjustment_action( "SpokeAction",
1930                                          _("Spoke ratio"), _("Spoke ratio:"),
1931                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1932                                          // Base radius is the same for the closest handle.
1933                                          _("Base radius to tip radius ratio"),
1934                                          "tools.shapes.star", "proportion", 0.5,
1935                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1936                                          0.01, 1.0, 0.01, 0.1,
1937                                          labels, values, G_N_ELEMENTS(labels),
1938                                          sp_stb_proportion_value_changed );
1939         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1940         g_object_set_data( holder, "prop_action", eact );
1941         }
1943         if ( !isFlatSided ) {
1944             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1945         } else {
1946             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1947         }
1949         /* Roundedness */
1950         {
1951         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1952         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1953         eact = create_adjustment_action( "RoundednessAction",
1954                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1955                                          "tools.shapes.star", "rounded", 0.0,
1956                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1957                                          -10.0, 10.0, 0.01, 0.1,
1958                                          labels, values, G_N_ELEMENTS(labels),
1959                                          sp_stb_rounded_value_changed );
1960         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1961         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1962         }
1964         /* Randomization */
1965         {
1966         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1967         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1968         eact = create_adjustment_action( "RandomizationAction",
1969                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1970                                          "tools.shapes.star", "randomized", 0.0,
1971                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1972                                          -10.0, 10.0, 0.001, 0.01,
1973                                          labels, values, G_N_ELEMENTS(labels),
1974                                          sp_stb_randomized_value_changed, 0.1, 3 );
1975         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1976         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1977         }
1978     }
1980     {
1981         /* Reset */
1982         {
1983             GtkAction* act = gtk_action_new( "StarResetAction",
1984                                              _("Defaults"),
1985                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1986                                              GTK_STOCK_CLEAR );
1987             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1988             gtk_action_group_add_action( mainActions, act );
1989             gtk_action_set_sensitive( act, TRUE );
1990         }
1991     }
1993     sigc::connection *connection = new sigc::connection(
1994         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1995         );
1996     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1997     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2001 //########################
2002 //##       Rect         ##
2003 //########################
2005 static void sp_rtb_sensitivize( GObject *tbl )
2007     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2008     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2009     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2011     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2012         gtk_action_set_sensitive( not_rounded, FALSE );
2013     } else {
2014         gtk_action_set_sensitive( not_rounded, TRUE );
2015     }
2019 static void
2020 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2021                           void (*setter)(SPRect *, gdouble))
2023     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2025     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2026     SPUnit const *unit = tracker->getActiveUnit();
2028     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2029         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2030     }
2032     // quit if run by the attr_changed listener
2033     if (g_object_get_data( tbl, "freeze" )) {
2034         return;
2035     }
2037     // in turn, prevent listener from responding
2038     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2040     bool modmade = false;
2041     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2042     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2043         if (SP_IS_RECT(items->data)) {
2044             if (adj->value != 0) {
2045                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2046             } else {
2047                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2048             }
2049             modmade = true;
2050         }
2051     }
2053     sp_rtb_sensitivize( tbl );
2055     if (modmade) {
2056         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2057                                    _("Change rectangle"));
2058     }
2060     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2063 static void
2064 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2066     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2069 static void
2070 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2072     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2075 static void
2076 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2078     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2081 static void
2082 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2084     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2089 static void
2090 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2092     GtkAdjustment *adj = 0;
2094     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2095     gtk_adjustment_set_value(adj, 0.0);
2096     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2097     gtk_adjustment_value_changed(adj);
2099     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2100     gtk_adjustment_set_value(adj, 0.0);
2101     gtk_adjustment_value_changed(adj);
2103     sp_rtb_sensitivize( obj );
2106 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2107                                        gchar const */*old_value*/, gchar const */*new_value*/,
2108                                        bool /*is_interactive*/, gpointer data)
2110     GObject *tbl = G_OBJECT(data);
2112     // quit if run by the _changed callbacks
2113     if (g_object_get_data( tbl, "freeze" )) {
2114         return;
2115     }
2117     // in turn, prevent callbacks from responding
2118     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2120     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2121     SPUnit const *unit = tracker->getActiveUnit();
2123     gpointer item = g_object_get_data( tbl, "item" );
2124     if (item && SP_IS_RECT(item)) {
2125         {
2126             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2127             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2128             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2129         }
2131         {
2132             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2133             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2134             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2135         }
2137         {
2138             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2139             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2140             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2141         }
2143         {
2144             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2145             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2146             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2147         }
2148     }
2150     sp_rtb_sensitivize( tbl );
2152     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2156 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2157     NULL, /* child_added */
2158     NULL, /* child_removed */
2159     rect_tb_event_attr_changed,
2160     NULL, /* content_changed */
2161     NULL  /* order_changed */
2162 };
2164 /**
2165  *  \param selection should not be NULL.
2166  */
2167 static void
2168 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2170     int n_selected = 0;
2171     Inkscape::XML::Node *repr = NULL;
2172     SPItem *item = NULL;
2174     if ( g_object_get_data( tbl, "repr" ) ) {
2175         g_object_set_data( tbl, "item", NULL );
2176     }
2177     purge_repr_listener( tbl, tbl );
2179     for (GSList const *items = selection->itemList();
2180          items != NULL;
2181          items = items->next) {
2182         if (SP_IS_RECT((SPItem *) items->data)) {
2183             n_selected++;
2184             item = (SPItem *) items->data;
2185             repr = SP_OBJECT_REPR(item);
2186         }
2187     }
2189     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2191     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2193     if (n_selected == 0) {
2194         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2196         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2197         gtk_action_set_sensitive(w, FALSE);
2198         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2199         gtk_action_set_sensitive(h, FALSE);
2201     } else if (n_selected == 1) {
2202         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2203         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2205         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2206         gtk_action_set_sensitive(w, TRUE);
2207         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2208         gtk_action_set_sensitive(h, TRUE);
2210         if (repr) {
2211             g_object_set_data( tbl, "repr", repr );
2212             g_object_set_data( tbl, "item", item );
2213             Inkscape::GC::anchor(repr);
2214             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2215             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2216         }
2217     } else {
2218         // FIXME: implement averaging of all parameters for multiple selected
2219         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2220         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2221         sp_rtb_sensitivize( tbl );
2222     }
2226 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2228     EgeAdjustmentAction* eact = 0;
2230     {
2231         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2232         ege_output_action_set_use_markup( act, TRUE );
2233         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2234         g_object_set_data( holder, "mode_action", act );
2235     }
2237     // rx/ry units menu: create
2238     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2239     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2240     // fixme: add % meaning per cent of the width/height
2241     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2242     g_object_set_data( holder, "tracker", tracker );
2244     /* W */
2245     {
2246         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2247         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2248         eact = create_adjustment_action( "RectWidthAction",
2249                                          _("Width"), _("W:"), _("Width of rectangle"),
2250                                          "tools.shapes.rect", "width", 0,
2251                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2252                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2253                                          labels, values, G_N_ELEMENTS(labels),
2254                                          sp_rtb_width_value_changed );
2255         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2256         g_object_set_data( holder, "width_action", eact );
2257         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2258         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2259     }
2261     /* H */
2262     {
2263         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2264         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2265         eact = create_adjustment_action( "RectHeightAction",
2266                                          _("Height"), _("H:"), _("Height of rectangle"),
2267                                          "tools.shapes.rect", "height", 0,
2268                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2269                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2270                                          labels, values, G_N_ELEMENTS(labels),
2271                                          sp_rtb_height_value_changed );
2272         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2273         g_object_set_data( holder, "height_action", eact );
2274         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2275         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2276     }
2278     /* rx */
2279     {
2280         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2281         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2282         eact = create_adjustment_action( "RadiusXAction",
2283                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2284                                          "tools.shapes.rect", "rx", 0,
2285                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2286                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2287                                          labels, values, G_N_ELEMENTS(labels),
2288                                          sp_rtb_rx_value_changed);
2289         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2290         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2291     }
2293     /* ry */
2294     {
2295         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2296         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2297         eact = create_adjustment_action( "RadiusYAction",
2298                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2299                                          "tools.shapes.rect", "ry", 0,
2300                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2301                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2302                                          labels, values, G_N_ELEMENTS(labels),
2303                                          sp_rtb_ry_value_changed);
2304         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2305         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2306     }
2308     // add the units menu
2309     {
2310         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2311         gtk_action_group_add_action( mainActions, act );
2312     }
2314     /* Reset */
2315     {
2316         InkAction* inky = ink_action_new( "RectResetAction",
2317                                           _("Not rounded"),
2318                                           _("Make corners sharp"),
2319                                           "squared_corner",
2320                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2321         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2322         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2323         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2324         g_object_set_data( holder, "not_rounded", inky );
2325     }
2327     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2328     sp_rtb_sensitivize( holder );
2330     sigc::connection *connection = new sigc::connection(
2331         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2332         );
2333     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2334     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2337 //########################
2338 //##       3D Box       ##
2339 //########################
2341 // normalize angle so that it lies in the interval [0,360]
2342 static double box3d_normalize_angle (double a) {
2343     double angle = a + ((int) (a/360.0))*360;
2344     if (angle < 0) {
2345         angle += 360.0;
2346     }
2347     return angle;
2350 static void
2351 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2352                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2353     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2354     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2355     //       are reset).
2356     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2358     if (is_infinite) {
2359         gtk_toggle_action_set_active(tact, TRUE);
2360         gtk_action_set_sensitive(act, TRUE);
2362         double angle = persp3d_get_infinite_angle(persp, axis);
2363         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2364             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2365         }
2366     } else {
2367         gtk_toggle_action_set_active(tact, FALSE);
2368         gtk_action_set_sensitive(act, FALSE);
2369     }
2372 static void
2373 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2374     if (!persp_repr) {
2375         g_print ("No perspective given to box3d_resync_toolbar().\n");
2376         return;
2377     }
2379     GtkWidget *tbl = GTK_WIDGET(data);
2380     GtkAdjustment *adj = 0;
2381     GtkAction *act = 0;
2382     GtkToggleAction *tact = 0;
2383     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2384     {
2385         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2386         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2387         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2389         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2390     }
2391     {
2392         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2393         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2394         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2396         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2397     }
2398     {
2399         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2400         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2401         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2403         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2404     }
2407 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2408                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2409                                                   bool /*is_interactive*/, gpointer data)
2411     GtkWidget *tbl = GTK_WIDGET(data);
2413     // quit if run by the attr_changed listener
2414     // note: it used to work without the differently called freeze_ attributes (here and in
2415     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2416     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2417         return;
2418     }
2420     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2421     // sp_document_maybe_done() when the document is undo insensitive)
2422     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2424     // TODO: Only update the appropriate part of the toolbar
2425 //    if (!strcmp(name, "inkscape:vp_z")) {
2426         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2427 //    }
2429     Persp3D *persp = persp3d_get_from_repr(repr);
2430     persp3d_update_box_reprs(persp);
2432     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2435 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2437     NULL, /* child_added */
2438     NULL, /* child_removed */
2439     box3d_persp_tb_event_attr_changed,
2440     NULL, /* content_changed */
2441     NULL  /* order_changed */
2442 };
2444 /**
2445  *  \param selection Should not be NULL.
2446  */
2447 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2448 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2449 static void
2450 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2452     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2453     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2454     // update the perspectives with infinite VPs and leave the other ones untouched).
2456     Inkscape::XML::Node *persp_repr = NULL;
2457     purge_repr_listener(tbl, tbl);
2459     SPItem *item = selection->singleItem();
2460     if (item && SP_IS_BOX3D(item)) {
2461         // FIXME: Also deal with multiple selected boxes
2462         SPBox3D *box = SP_BOX3D(item);
2463         Persp3D *persp = box3d_get_perspective(box);
2464         persp_repr = SP_OBJECT_REPR(persp);
2465         if (persp_repr) {
2466             g_object_set_data(tbl, "repr", persp_repr);
2467             Inkscape::GC::anchor(persp_repr);
2468             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2469             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2470         }
2472         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2473         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2474     
2475         box3d_resync_toolbar(persp_repr, tbl);
2476     }
2479 static void
2480 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2482     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2483     SPDocument *document = sp_desktop_document(desktop);
2485     // quit if run by the attr_changed listener
2486     // note: it used to work without the differently called freeze_ attributes (here and in
2487     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2488     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2489         return;
2490     }
2492     // in turn, prevent listener from responding
2493     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2495     //Persp3D *persp = document->current_persp3d;
2496     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2497     if (sel_persps.empty()) {
2498         // this can happen when the document is created; we silently ignore it
2499         return;
2500     }
2501     Persp3D *persp = *(sel_persps.begin());
2503     persp->tmat.set_infinite_direction (axis, adj->value);
2504     SP_OBJECT(persp)->updateRepr();
2506     // TODO: use the correct axis here, too
2507     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2509     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2513 static void
2514 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2516     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2519 static void
2520 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2522     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2525 static void
2526 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2528     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2532 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2534     // TODO: Take all selected perspectives into account
2535     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2536     if (sel_persps.empty()) {
2537         // this can happen when the document is created; we silently ignore it
2538         return;
2539     }
2540     Persp3D *persp = *(sel_persps.begin());
2542     bool set_infinite = gtk_toggle_action_get_active(act);
2543     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2546 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2548     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2551 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2553     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2556 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2558     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2561 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2563     EgeAdjustmentAction* eact = 0;
2564     SPDocument *document = sp_desktop_document (desktop);
2565     Persp3D *persp = document->current_persp3d;
2567     EgeAdjustmentAction* box3d_angle_x = 0;
2568     EgeAdjustmentAction* box3d_angle_y = 0;
2569     EgeAdjustmentAction* box3d_angle_z = 0;
2571     /* Angle X */
2572     {
2573         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2574         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2575         eact = create_adjustment_action( "3DBoxAngleXAction",
2576                                          _("Angle in X direction"), _("Angle X:"),
2577                                          // Translators: PL is short for 'perspective line'
2578                                          _("Angle of PLs in X direction"),
2579                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2580                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2581                                          -360.0, 360.0, 1.0, 10.0,
2582                                          labels, values, G_N_ELEMENTS(labels),
2583                                          box3d_angle_x_value_changed );
2584         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2585         g_object_set_data( holder, "box3d_angle_x_action", eact );
2586         box3d_angle_x = eact;
2587     }
2589     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2590         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2591     } else {
2592         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2593     }
2596     /* VP X state */
2597     {
2598         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2599                                                       // Translators: VP is short for 'vanishing point'
2600                                                       _("State of VP in X direction"),
2601                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2602                                                       "toggle_vp_x",
2603                                                       Inkscape::ICON_SIZE_DECORATION );
2604         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2605         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2606         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2607         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2608         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2609     }
2611     /* Angle Y */
2612     {
2613         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2614         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2615         eact = create_adjustment_action( "3DBoxAngleYAction",
2616                                          _("Angle in Y direction"), _("Angle Y:"),
2617                                          // Translators: PL is short for 'perspective line'
2618                                          _("Angle of PLs in Y direction"),
2619                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2620                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2621                                          -360.0, 360.0, 1.0, 10.0,
2622                                          labels, values, G_N_ELEMENTS(labels),
2623                                          box3d_angle_y_value_changed );
2624         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2625         g_object_set_data( holder, "box3d_angle_y_action", eact );
2626         box3d_angle_y = eact;
2627     }
2629     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2630         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2631     } else {
2632         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2633     }
2635     /* VP Y state */
2636     {
2637         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2638                                                       // Translators: VP is short for 'vanishing point'
2639                                                       _("State of VP in Y direction"),
2640                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2641                                                       "toggle_vp_y",
2642                                                       Inkscape::ICON_SIZE_DECORATION );
2643         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2644         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2645         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2646         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2647         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2648     }
2650     /* Angle Z */
2651     {
2652         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2653         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2654         eact = create_adjustment_action( "3DBoxAngleZAction",
2655                                          _("Angle in Z direction"), _("Angle Z:"),
2656                                          // Translators: PL is short for 'perspective line'
2657                                          _("Angle of PLs in Z direction"),
2658                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2659                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2660                                          -360.0, 360.0, 1.0, 10.0,
2661                                          labels, values, G_N_ELEMENTS(labels),
2662                                          box3d_angle_z_value_changed );
2663         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2664         g_object_set_data( holder, "box3d_angle_z_action", eact );
2665         box3d_angle_z = eact;
2666     }
2668     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2669         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2670     } else {
2671         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2672     }
2674     /* VP Z state */
2675     {
2676         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2677                                                       // Translators: VP is short for 'vanishing point'
2678                                                       _("State of VP in Z direction"),
2679                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2680                                                       "toggle_vp_z",
2681                                                       Inkscape::ICON_SIZE_DECORATION );
2682         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2683         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2684         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2685         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2686         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2687     }
2689     sigc::connection *connection = new sigc::connection(
2690         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2691        );
2692     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2693     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2696 //########################
2697 //##       Spiral       ##
2698 //########################
2700 static void
2701 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2703     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2705     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2706         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2707     }
2709     // quit if run by the attr_changed listener
2710     if (g_object_get_data( tbl, "freeze" )) {
2711         return;
2712     }
2714     // in turn, prevent listener from responding
2715     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2717     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2719     bool modmade = false;
2720     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2721          items != NULL;
2722          items = items->next)
2723     {
2724         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2725             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2726             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2727             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2728             modmade = true;
2729         }
2730     }
2732     g_free(namespaced_name);
2734     if (modmade) {
2735         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2736                                    _("Change spiral"));
2737     }
2739     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2742 static void
2743 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2745     sp_spl_tb_value_changed(adj, tbl, "revolution");
2748 static void
2749 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2751     sp_spl_tb_value_changed(adj, tbl, "expansion");
2754 static void
2755 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2757     sp_spl_tb_value_changed(adj, tbl, "t0");
2760 static void
2761 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2763     GtkWidget *tbl = GTK_WIDGET(obj);
2765     GtkAdjustment *adj;
2767     // fixme: make settable
2768     gdouble rev = 5;
2769     gdouble exp = 1.0;
2770     gdouble t0 = 0.0;
2772     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2773     gtk_adjustment_set_value(adj, rev);
2774     gtk_adjustment_value_changed(adj);
2776     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2777     gtk_adjustment_set_value(adj, exp);
2778     gtk_adjustment_value_changed(adj);
2780     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2781     gtk_adjustment_set_value(adj, t0);
2782     gtk_adjustment_value_changed(adj);
2784     spinbutton_defocus(GTK_OBJECT(tbl));
2788 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2789                                          gchar const */*old_value*/, gchar const */*new_value*/,
2790                                          bool /*is_interactive*/, gpointer data)
2792     GtkWidget *tbl = GTK_WIDGET(data);
2794     // quit if run by the _changed callbacks
2795     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2796         return;
2797     }
2799     // in turn, prevent callbacks from responding
2800     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2802     GtkAdjustment *adj;
2803     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2804     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2806     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2807     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2809     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2810     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2812     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2816 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2817     NULL, /* child_added */
2818     NULL, /* child_removed */
2819     spiral_tb_event_attr_changed,
2820     NULL, /* content_changed */
2821     NULL  /* order_changed */
2822 };
2824 static void
2825 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2827     int n_selected = 0;
2828     Inkscape::XML::Node *repr = NULL;
2830     purge_repr_listener( tbl, tbl );
2832     for (GSList const *items = selection->itemList();
2833          items != NULL;
2834          items = items->next)
2835     {
2836         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2837             n_selected++;
2838             repr = SP_OBJECT_REPR((SPItem *) items->data);
2839         }
2840     }
2842     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2844     if (n_selected == 0) {
2845         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2846     } else if (n_selected == 1) {
2847         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2849         if (repr) {
2850             g_object_set_data( tbl, "repr", repr );
2851             Inkscape::GC::anchor(repr);
2852             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2853             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2854         }
2855     } else {
2856         // FIXME: implement averaging of all parameters for multiple selected
2857         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2858         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2859     }
2863 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2865     EgeAdjustmentAction* eact = 0;
2867     {
2868         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2869         ege_output_action_set_use_markup( act, TRUE );
2870         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2871         g_object_set_data( holder, "mode_action", act );
2872     }
2874     /* Revolution */
2875     {
2876         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2877         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2878         eact = create_adjustment_action( "SpiralRevolutionAction",
2879                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2880                                          "tools.shapes.spiral", "revolution", 3.0,
2881                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2882                                          0.01, 1024.0, 0.1, 1.0,
2883                                          labels, values, G_N_ELEMENTS(labels),
2884                                          sp_spl_tb_revolution_value_changed, 1, 2);
2885         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2886     }
2888     /* Expansion */
2889     {
2890         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2891         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2892         eact = create_adjustment_action( "SpiralExpansionAction",
2893                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2894                                          "tools.shapes.spiral", "expansion", 1.0,
2895                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2896                                          0.0, 1000.0, 0.01, 1.0,
2897                                          labels, values, G_N_ELEMENTS(labels),
2898                                          sp_spl_tb_expansion_value_changed);
2899         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2900     }
2902     /* T0 */
2903     {
2904         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2905         gdouble values[] = {0, 0.5, 0.9};
2906         eact = create_adjustment_action( "SpiralT0Action",
2907                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2908                                          "tools.shapes.spiral", "t0", 0.0,
2909                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2910                                          0.0, 0.999, 0.01, 1.0,
2911                                          labels, values, G_N_ELEMENTS(labels),
2912                                          sp_spl_tb_t0_value_changed);
2913         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2914     }
2916     /* Reset */
2917     {
2918         InkAction* inky = ink_action_new( "SpiralResetAction",
2919                                           _("Defaults"),
2920                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2921                                           GTK_STOCK_CLEAR,
2922                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2923         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2924         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2925     }
2928     sigc::connection *connection = new sigc::connection(
2929         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2930         );
2931     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2932     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2935 //########################
2936 //##     Pen/Pencil    ##
2937 //########################
2940 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2942     // Put stuff here
2945 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2947     // Put stuff here
2950 //########################
2951 //##       Tweak        ##
2952 //########################
2954 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2956     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2959 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2961     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2964 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2966     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2969 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2971     int mode = ege_select_one_action_get_active( act );
2972     prefs_set_int_attribute("tools.tweak", "mode", mode);
2974     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2975     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2976     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2977     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2978     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2979     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2980     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2981         if (doh) gtk_action_set_sensitive (doh, TRUE);
2982         if (dos) gtk_action_set_sensitive (dos, TRUE);
2983         if (dol) gtk_action_set_sensitive (dol, TRUE);
2984         if (doo) gtk_action_set_sensitive (doo, TRUE);
2985         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2986         if (fid) gtk_action_set_sensitive (fid, FALSE);
2987     } else {
2988         if (doh) gtk_action_set_sensitive (doh, FALSE);
2989         if (dos) gtk_action_set_sensitive (dos, FALSE);
2990         if (dol) gtk_action_set_sensitive (dol, FALSE);
2991         if (doo) gtk_action_set_sensitive (doo, FALSE);
2992         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2993         if (fid) gtk_action_set_sensitive (fid, TRUE);
2994     }
2997 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2999     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3002 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3003     bool show = gtk_toggle_action_get_active( act );
3004     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3006 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3007     bool show = gtk_toggle_action_get_active( act );
3008     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3010 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3011     bool show = gtk_toggle_action_get_active( act );
3012     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3014 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3015     bool show = gtk_toggle_action_get_active( act );
3016     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3019 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3021     {
3022         /* Width */
3023         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3024         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3025         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3026                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3027                                                               "tools.tweak", "width", 15,
3028                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3029                                                               1, 100, 1.0, 10.0,
3030                                                               labels, values, G_N_ELEMENTS(labels),
3031                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3032         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3033         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3034     }
3037     {
3038         /* Force */
3039         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3040         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3041         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3042                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3043                                                               "tools.tweak", "force", 20,
3044                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3045                                                               1, 100, 1.0, 10.0,
3046                                                               labels, values, G_N_ELEMENTS(labels),
3047                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3048         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3049         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3050     }
3052     /* Mode */
3053     {
3054         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3056         GtkTreeIter iter;
3057         gtk_list_store_append( model, &iter );
3058         gtk_list_store_set( model, &iter,
3059                             0, _("Push mode"),
3060                             1, _("Push parts of paths in any direction"),
3061                             2, "tweak_push_mode",
3062                             -1 );
3064         gtk_list_store_append( model, &iter );
3065         gtk_list_store_set( model, &iter,
3066                             0, _("Shrink mode"),
3067                             1, _("Shrink (inset) parts of paths"),
3068                             2, "tweak_shrink_mode",
3069                             -1 );
3071         gtk_list_store_append( model, &iter );
3072         gtk_list_store_set( model, &iter,
3073                             0, _("Grow mode"),
3074                             1, _("Grow (outset) parts of paths"),
3075                             2, "tweak_grow_mode",
3076                             -1 );
3078         gtk_list_store_append( model, &iter );
3079         gtk_list_store_set( model, &iter,
3080                             0, _("Attract mode"),
3081                             1, _("Attract parts of paths towards cursor"),
3082                             2, "tweak_attract_mode",
3083                             -1 );
3085         gtk_list_store_append( model, &iter );
3086         gtk_list_store_set( model, &iter,
3087                             0, _("Repel mode"),
3088                             1, _("Repel parts of paths from cursor"),
3089                             2, "tweak_repel_mode",
3090                             -1 );
3092         gtk_list_store_append( model, &iter );
3093         gtk_list_store_set( model, &iter,
3094                             0, _("Roughen mode"),
3095                             1, _("Roughen parts of paths"),
3096                             2, "tweak_roughen_mode",
3097                             -1 );
3099         gtk_list_store_append( model, &iter );
3100         gtk_list_store_set( model, &iter,
3101                             0, _("Color paint mode"),
3102                             1, _("Paint the tool's color upon selected objects"),
3103                             2, "tweak_colorpaint_mode",
3104                             -1 );
3106         gtk_list_store_append( model, &iter );
3107         gtk_list_store_set( model, &iter,
3108                             0, _("Color jitter mode"),
3109                             1, _("Jitter the colors of selected objects"),
3110                             2, "tweak_colorjitter_mode",
3111                             -1 );
3113         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3114         g_object_set( act, "short_label", _("Mode:"), NULL );
3115         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3116         g_object_set_data( holder, "mode_action", act );
3118         ege_select_one_action_set_appearance( act, "full" );
3119         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3120         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3121         ege_select_one_action_set_icon_column( act, 2 );
3122         ege_select_one_action_set_tooltip_column( act, 1  );
3124         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3125         ege_select_one_action_set_active( act, mode );
3126         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3128         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3129     }
3131     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3133     {
3134         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3135         ege_output_action_set_use_markup( act, TRUE );
3136         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3137         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3138             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3139         g_object_set_data( holder, "tweak_channels_label", act);
3140     }
3142     {
3143         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3144                                                       _("Hue"),
3145                                                       _("In color mode, act on objects' hue"),
3146                                                       NULL,
3147                                                       Inkscape::ICON_SIZE_DECORATION );
3148         g_object_set( act, "short_label", _("H"), NULL );
3149         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3150         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3151         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3152         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3153             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3154         g_object_set_data( holder, "tweak_doh", act);
3155     }
3156     {
3157         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3158                                                       _("Saturation"),
3159                                                       _("In color mode, act on objects' saturation"),
3160                                                       NULL,
3161                                                       Inkscape::ICON_SIZE_DECORATION );
3162         g_object_set( act, "short_label", _("S"), NULL );
3163         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3164         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3165         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3166         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3167             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3168         g_object_set_data( holder, "tweak_dos", act );
3169     }
3170     {
3171         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3172                                                       _("Lightness"),
3173                                                       _("In color mode, act on objects' lightness"),
3174                                                       NULL,
3175                                                       Inkscape::ICON_SIZE_DECORATION );
3176         g_object_set( act, "short_label", _("L"), NULL );
3177         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3178         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3179         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3180         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3181             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3182         g_object_set_data( holder, "tweak_dol", act );
3183     }
3184     {
3185         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3186                                                       _("Opacity"),
3187                                                       _("In color mode, act on objects' opacity"),
3188                                                       NULL,
3189                                                       Inkscape::ICON_SIZE_DECORATION );
3190         g_object_set( act, "short_label", _("O"), NULL );
3191         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3192         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3193         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3194         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3195             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3196         g_object_set_data( holder, "tweak_doo", act );
3197     }
3199     {   /* Fidelity */
3200         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3201         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3202         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3203                                                               _("Fidelity"), _("Fidelity:"),
3204                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3205                                                               "tools.tweak", "fidelity", 50,
3206                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3207                                                               1, 100, 1.0, 10.0,
3208                                                               labels, values, G_N_ELEMENTS(labels),
3209                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3210         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3211         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3212         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3213             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3214         g_object_set_data( holder, "tweak_fidelity", eact );
3215     }
3218     /* Use Pressure button */
3219     {
3220         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3221                                                       _("Pressure"),
3222                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3223                                                       "use_pressure",
3224                                                       Inkscape::ICON_SIZE_DECORATION );
3225         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3226         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3227         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3228     }
3233 //########################
3234 //##     Calligraphy    ##
3235 //########################
3237 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3239     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3242 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3244     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3247 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3249     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3252 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3254     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3257 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3259     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3262 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3264     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3267 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3269     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3272 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3274     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3277 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3279     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3282 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3284     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3287 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3289     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3291     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3294 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3296     // FIXME: make defaults settable via Inkscape Options
3297     struct KeyValue {
3298         char const *key;
3299         double value;
3300     } const key_values[] = {
3301         {"mass", 0.02},
3302         {"wiggle", 0.0},
3303         {"angle", 30.0},
3304         {"width", 15},
3305         {"thinning", 0.1},
3306         {"tremor", 0.0},
3307         {"flatness", 0.9},
3308         {"cap_rounding", 0.0}
3309     };
3311     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3312         KeyValue const &kv = key_values[i];
3313         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3314         if ( adj ) {
3315             gtk_adjustment_set_value(adj, kv.value);
3316         }
3317     }
3321 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3323     {
3324         EgeAdjustmentAction* calligraphy_angle = 0;
3326         {
3327         /* Width */
3328         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3329         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3330         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3331                                                               _("Pen Width"), _("Width:"),
3332                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3333                                                               "tools.calligraphic", "width", 15,
3334                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3335                                                               1, 100, 1.0, 10.0,
3336                                                               labels, values, G_N_ELEMENTS(labels),
3337                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3338         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3339         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3340         }
3342         {
3343         /* Thinning */
3344             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3345             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3346         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3347                                                               _("Stroke Thinning"), _("Thinning:"),
3348                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3349                                                               "tools.calligraphic", "thinning", 0.1,
3350                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3351                                                               -1.0, 1.0, 0.01, 0.1,
3352                                                               labels, values, G_N_ELEMENTS(labels),
3353                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3354         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3355         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3356         }
3358         {
3359         /* Angle */
3360         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3361         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3362         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3363                                                               _("Pen Angle"), _("Angle:"),
3364                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3365                                                               "tools.calligraphic", "angle", 30,
3366                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3367                                                               -90.0, 90.0, 1.0, 10.0,
3368                                                               labels, values, G_N_ELEMENTS(labels),
3369                                                               sp_ddc_angle_value_changed, 1, 0 );
3370         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3371         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3372         calligraphy_angle = eact;
3373         }
3375         {
3376         /* Fixation */
3377             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3378         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3379         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3380                                                               _("Fixation"), _("Fixation:"),
3381                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3382                                                               "tools.calligraphic", "flatness", 0.9,
3383                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3384                                                               0.0, 1.0, 0.01, 0.1,
3385                                                               labels, values, G_N_ELEMENTS(labels),
3386                                                               sp_ddc_flatness_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         /* Cap Rounding */
3393             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3394         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3395         // TRANSLATORS: "cap" means "end" (both start and finish) here
3396         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3397                                                               _("Cap rounding"), _("Caps:"),
3398                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3399                                                               "tools.calligraphic", "cap_rounding", 0.0,
3400                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3401                                                               0.0, 5.0, 0.01, 0.1,
3402                                                               labels, values, G_N_ELEMENTS(labels),
3403                                                               sp_ddc_cap_rounding_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         /* Tremor */
3410             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3411         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3412         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3413                                                               _("Stroke Tremor"), _("Tremor:"),
3414                                                               _("Increase to make strokes rugged and trembling"),
3415                                                               "tools.calligraphic", "tremor", 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_tremor_value_changed, 0.01, 2 );
3421         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3422         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3423         }
3425         {
3426         /* Wiggle */
3427         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3428         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3429         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3430                                                               _("Pen Wiggle"), _("Wiggle:"),
3431                                                               _("Increase to make the pen waver and wiggle"),
3432                                                               "tools.calligraphic", "wiggle", 0.0,
3433                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3434                                                               0.0, 1.0, 0.01, 0.1,
3435                                                               labels, values, G_N_ELEMENTS(labels),
3436                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3437         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3438         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3439         }
3441         {
3442         /* Mass */
3443             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3444         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3445         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3446                                                               _("Pen Mass"), _("Mass:"),
3447                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3448                                                               "tools.calligraphic", "mass", 0.02,
3449                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3450                                                               0.0, 1.0, 0.01, 0.1,
3451                                                               labels, values, G_N_ELEMENTS(labels),
3452                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3453         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3454         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3455         }
3458         /* Trace Background button */
3459         {
3460             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3461                                                           _("Trace Background"),
3462                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3463                                                           "trace_background",
3464                                                           Inkscape::ICON_SIZE_DECORATION );
3465             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3466             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3467             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3468         }
3470         /* Use Pressure button */
3471         {
3472             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3473                                                           _("Pressure"),
3474                                                           _("Use the pressure of the input device to alter the width of the pen"),
3475                                                           "use_pressure",
3476                                                           Inkscape::ICON_SIZE_DECORATION );
3477             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3478             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3479             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3480         }
3482         /* Use Tilt button */
3483         {
3484             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3485                                                           _("Tilt"),
3486                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3487                                                           "use_tilt",
3488                                                           Inkscape::ICON_SIZE_DECORATION );
3489             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3490             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3491             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3492             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3493         }
3495         /* Reset */
3496         {
3497             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3498                                              _("Defaults"),
3499                                              _("Reset all parameters to defaults"),
3500                                              GTK_STOCK_CLEAR );
3501             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3502             gtk_action_group_add_action( mainActions, act );
3503             gtk_action_set_sensitive( act, TRUE );
3504         }
3505     }
3509 //########################
3510 //##    Circle / Arc    ##
3511 //########################
3513 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3515     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3516     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3518     if (v1 == 0 && v2 == 0) {
3519         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3520             gtk_action_set_sensitive( ocb, FALSE );
3521             gtk_action_set_sensitive( make_whole, FALSE );
3522         }
3523     } else {
3524         gtk_action_set_sensitive( ocb, TRUE );
3525         gtk_action_set_sensitive( make_whole, TRUE );
3526     }
3529 static void
3530 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3532     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3534     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3535         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3536     }
3538     // quit if run by the attr_changed listener
3539     if (g_object_get_data( tbl, "freeze" )) {
3540         return;
3541     }
3543     // in turn, prevent listener from responding
3544     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3546     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3548     bool modmade = false;
3549     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3550          items != NULL;
3551          items = items->next)
3552     {
3553         SPItem *item = SP_ITEM(items->data);
3555         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3557             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3558             SPArc *arc = SP_ARC(item);
3560             if (!strcmp(value_name, "start"))
3561                 ge->start = (adj->value * M_PI)/ 180;
3562             else
3563                 ge->end = (adj->value * M_PI)/ 180;
3565             sp_genericellipse_normalize(ge);
3566             ((SPObject *)arc)->updateRepr();
3567             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3569             modmade = true;
3570         }
3571     }
3573     g_free(namespaced_name);
3575     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3577     sp_arctb_sensitivize( tbl, adj->value, other->value );
3579     if (modmade) {
3580         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3581                                    _("Arc: Change start/end"));
3582     }
3584     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3588 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3590     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3593 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3595     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3598 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3600     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3601     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3602         if ( ege_select_one_action_get_active( act ) != 0 ) {
3603             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3604         } else {
3605             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3606         }
3607     }
3609     // quit if run by the attr_changed listener
3610     if (g_object_get_data( tbl, "freeze" )) {
3611         return;
3612     }
3614     // in turn, prevent listener from responding
3615     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3617     bool modmade = false;
3619     if ( ege_select_one_action_get_active(act) != 0 ) {
3620         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3621              items != NULL;
3622              items = items->next)
3623         {
3624             if (SP_IS_ARC((SPItem *) items->data)) {
3625                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3626                 repr->setAttribute("sodipodi:open", "true");
3627                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3628                 modmade = true;
3629             }
3630         }
3631     } else {
3632         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3633              items != NULL;
3634              items = items->next)
3635         {
3636             if (SP_IS_ARC((SPItem *) items->data))    {
3637                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3638                 repr->setAttribute("sodipodi:open", NULL);
3639                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3640                 modmade = true;
3641             }
3642         }
3643     }
3645     if (modmade) {
3646         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3647                                    _("Arc: Change open/closed"));
3648     }
3650     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3653 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3655     GtkAdjustment *adj;
3656     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3657     gtk_adjustment_set_value(adj, 0.0);
3658     gtk_adjustment_value_changed(adj);
3660     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3661     gtk_adjustment_set_value(adj, 0.0);
3662     gtk_adjustment_value_changed(adj);
3664     spinbutton_defocus( GTK_OBJECT(obj) );
3667 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3668                                       gchar const */*old_value*/, gchar const */*new_value*/,
3669                                       bool /*is_interactive*/, gpointer data)
3671     GObject *tbl = G_OBJECT(data);
3673     // quit if run by the _changed callbacks
3674     if (g_object_get_data( tbl, "freeze" )) {
3675         return;
3676     }
3678     // in turn, prevent callbacks from responding
3679     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3681     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3682     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3684     GtkAdjustment *adj1,*adj2;
3685     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3686     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3687     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3688     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3690     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3692     char const *openstr = NULL;
3693     openstr = repr->attribute("sodipodi:open");
3694     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3696     if (openstr) {
3697         ege_select_one_action_set_active( ocb, 1 );
3698     } else {
3699         ege_select_one_action_set_active( ocb, 0 );
3700     }
3702     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3705 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3706     NULL, /* child_added */
3707     NULL, /* child_removed */
3708     arc_tb_event_attr_changed,
3709     NULL, /* content_changed */
3710     NULL  /* order_changed */
3711 };
3714 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3716     int n_selected = 0;
3717     Inkscape::XML::Node *repr = NULL;
3719     purge_repr_listener( tbl, tbl );
3721     for (GSList const *items = selection->itemList();
3722          items != NULL;
3723          items = items->next)
3724     {
3725         if (SP_IS_ARC((SPItem *) items->data)) {
3726             n_selected++;
3727             repr = SP_OBJECT_REPR((SPItem *) items->data);
3728         }
3729     }
3731     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3733     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3734     if (n_selected == 0) {
3735         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3736     } else if (n_selected == 1) {
3737         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3738         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3740         if (repr) {
3741             g_object_set_data( tbl, "repr", repr );
3742             Inkscape::GC::anchor(repr);
3743             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3744             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3745         }
3746     } else {
3747         // FIXME: implement averaging of all parameters for multiple selected
3748         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3749         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3750         sp_arctb_sensitivize( tbl, 1, 0 );
3751     }
3755 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3757     EgeAdjustmentAction* eact = 0;
3760     {
3761         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3762         ege_output_action_set_use_markup( act, TRUE );
3763         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3764         g_object_set_data( holder, "mode_action", act );
3765     }
3767     /* Start */
3768     {
3769         eact = create_adjustment_action( "ArcStartAction",
3770                                          _("Start"), _("Start:"),
3771                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3772                                          "tools.shapes.arc", "start", 0.0,
3773                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3774                                          -360.0, 360.0, 1.0, 10.0,
3775                                          0, 0, 0,
3776                                          sp_arctb_start_value_changed);
3777         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3778     }
3780     /* End */
3781     {
3782         eact = create_adjustment_action( "ArcEndAction",
3783                                          _("End"), _("End:"),
3784                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3785                                          "tools.shapes.arc", "end", 0.0,
3786                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3787                                          -360.0, 360.0, 1.0, 10.0,
3788                                          0, 0, 0,
3789                                          sp_arctb_end_value_changed);
3790         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3791     }
3793     /* Segments / Pie checkbox */
3794     {
3795         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3797         GtkTreeIter iter;
3798         gtk_list_store_append( model, &iter );
3799         gtk_list_store_set( model, &iter,
3800                             0, _("Closed arc"),
3801                             1, _("Switch to segment (closed shape with two radii)"),
3802                             2, "circle_closed_arc",
3803                             -1 );
3805         gtk_list_store_append( model, &iter );
3806         gtk_list_store_set( model, &iter,
3807                             0, _("Open Arc"),
3808                             1, _("Switch to arc (unclosed shape)"),
3809                             2, "circle_open_arc",
3810                             -1 );
3812         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3813         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3814         g_object_set_data( holder, "open_action", act );
3816         ege_select_one_action_set_appearance( act, "full" );
3817         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3818         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3819         ege_select_one_action_set_icon_column( act, 2 );
3820         ege_select_one_action_set_tooltip_column( act, 1  );
3822         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3823         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3824         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3825         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3826     }
3828     /* Make Whole */
3829     {
3830         InkAction* inky = ink_action_new( "ArcResetAction",
3831                                           _("Make whole"),
3832                                           _("Make the shape a whole ellipse, not arc or segment"),
3833                                           "reset_circle",
3834                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3835         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3836         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3837         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3838         g_object_set_data( holder, "make_whole", inky );
3839     }
3841     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3842     // sensitivize make whole and open checkbox
3843     {
3844         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3845         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3846         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3847     }
3850     sigc::connection *connection = new sigc::connection(
3851         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3852         );
3853     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3854     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3860 // toggle button callbacks and updaters
3862 //########################
3863 //##      Dropper       ##
3864 //########################
3866 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3867     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3868     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3869     if ( set_action ) {
3870         if ( gtk_toggle_action_get_active( act ) ) {
3871             gtk_action_set_sensitive( set_action, TRUE );
3872         } else {
3873             gtk_action_set_sensitive( set_action, FALSE );
3874         }
3875     }
3877     spinbutton_defocus(GTK_OBJECT(tbl));
3880 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3881     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3882     spinbutton_defocus(GTK_OBJECT(tbl));
3886 /**
3887  * Dropper auxiliary toolbar construction and setup.
3888  *
3889  * TODO: Would like to add swatch of current color.
3890  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3891  *       can drag and drop places. Will provide a nice mixing palette.
3892  */
3893 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3895     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3897     {
3898         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3899                                                       _("Pick alpha"),
3900                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3901                                                       "color_alpha_get",
3902                                                       Inkscape::ICON_SIZE_DECORATION );
3903         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3904         g_object_set_data( holder, "pick_action", act );
3905         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3906         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3907     }
3909     {
3910         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3911                                                       _("Set alpha"),
3912                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3913                                                       "color_alpha_set",
3914                                                       Inkscape::ICON_SIZE_DECORATION );
3915         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3916         g_object_set_data( holder, "set_action", act );
3917         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3918         // make sure it's disabled if we're not picking alpha
3919         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3920         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3921     }
3925 //########################
3926 //##    Text Toolbox    ##
3927 //########################
3928 /*
3929 static void
3930 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3932     //Call back for letter sizing spinbutton
3935 static void
3936 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3938     //Call back for line height spinbutton
3941 static void
3942 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3944     //Call back for horizontal kerning spinbutton
3947 static void
3948 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3950     //Call back for vertical kerning spinbutton
3953 static void
3954 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3956     //Call back for letter rotation spinbutton
3957 }*/
3959 namespace {
3961 bool popdown_visible = false;
3962 bool popdown_hasfocus = false;
3964 void
3965 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3967     SPStyle *query =
3968         sp_style_new (SP_ACTIVE_DOCUMENT);
3969     
3970     int result_fontspec =
3971         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3973     int result_family =
3974         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3976     int result_style =
3977         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3979     int result_numbers =
3980         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3982     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3984     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3985     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3986     {
3987         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3989         if (repr)
3990         {
3991             sp_style_read_from_repr (query, repr);
3992         }
3993         else
3994         {
3995             return;
3996         }
3997     }
3999     if (query->text)
4000     {
4001         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4002             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4003             gtk_entry_set_text (GTK_ENTRY (entry), "");
4005         } else if (query->text->font_specification.value || query->text->font_family.value) {
4007             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4008             
4009             // Get the font that corresponds 
4010             Glib::ustring familyName;
4011             
4012             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4013             if (font) {
4014                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4015                 font->Unref();
4016                 font = NULL;
4017             }
4018             
4019             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4021             Gtk::TreePath path;
4022             try {
4023                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4024             } catch (...) {
4025                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4026                 return;
4027             }
4029             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4030             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4032             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4034             gtk_tree_selection_select_path (tselection, path.gobj());
4035             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4037             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4038         }
4040         //Size
4041         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4042         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4043         g_object_set_data (tbl, "size-block", gpointer(1));
4044         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4045         g_object_set_data (tbl, "size-block", gpointer(0));
4046         free (str);
4048         //Anchor
4049         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4050         {
4051             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4052             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4053             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4054             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4055         }
4056         else
4057         {
4058             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4059             {
4060                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4061                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4062                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4063                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4064             }
4065             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4066             {
4067                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4068                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4069                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4070                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4071             }
4072             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4073             {
4074                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4075                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4076                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4077                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4078             }
4079         }
4081         //Style
4082         {
4083             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4085             gboolean active = gtk_toggle_button_get_active (button);
4086             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4088             if (active != check)
4089             {
4090                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4091                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4092                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4093             }
4094         }
4096         {
4097             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4099             gboolean active = gtk_toggle_button_get_active (button);
4100             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4102             if (active != check)
4103             {
4104                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4105                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4106                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4107             }
4108         }
4110         //Orientation
4111         //locking both buttons, changing one affect all group (both)
4112         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4113         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4115         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4116         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4118         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4119         {
4120             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4121         }
4122         else
4123         {
4124             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4125         }
4126         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4127         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4128     }
4130     sp_style_unref(query);
4133 void
4134 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4136     sp_text_toolbox_selection_changed (selection, tbl);
4139 void
4140 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4142     sp_text_toolbox_selection_changed (NULL, tbl);
4145 void
4146 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4147                                 GObject             *tbl)
4149     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4150     GtkTreeModel *model = 0;
4151     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4152     GtkTreeIter   iter;
4153     char         *family = 0;
4155     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4156     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4158     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4159         return;
4160     }
4162     gtk_tree_model_get (model, &iter, 0, &family, -1);
4164     if (g_object_get_data (G_OBJECT (selection), "block"))
4165     {
4166         gtk_entry_set_text (GTK_ENTRY (entry), family);
4167         return;
4168     }
4170     gtk_entry_set_text (GTK_ENTRY (entry), family);
4172     SPStyle *query =
4173         sp_style_new (SP_ACTIVE_DOCUMENT);
4175     int result_fontspec =
4176         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4178     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4179     
4180     SPCSSAttr *css = sp_repr_css_attr_new ();
4181     
4182     
4183     // First try to get the font spec from the stored value
4184     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4186     if (fontSpec.empty()) {
4187         // Construct a new font specification if it does not yet exist
4188         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4189         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4190         fontFromStyle->Unref();
4191     }
4192     
4193     if (!fontSpec.empty()) {
4194         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4195         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4196             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4197             if (font) {
4198                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4199                 
4200                 // Set all the these just in case they were altered when finding the best
4201                 // match for the new family and old style...
4202                 
4203                 gchar c[256];
4204                 
4205                 font->Family(c, 256);
4206                 sp_repr_css_set_property (css, "font-family", c);
4207                 
4208                 font->Attribute( "weight", c, 256);
4209                 sp_repr_css_set_property (css, "font-weight", c);
4210                 
4211                 font->Attribute("style", c, 256);
4212                 sp_repr_css_set_property (css, "font-style", c);
4213                 
4214                 font->Attribute("stretch", c, 256);
4215                 sp_repr_css_set_property (css, "font-stretch", c);
4216                 
4217                 font->Attribute("variant", c, 256);
4218                 sp_repr_css_set_property (css, "font-variant", c);
4219                 
4220                 font->Unref();
4221             }
4222         }
4223     }
4225     // If querying returned nothing, set the default style of the tool (for new texts)
4226     if (result_fontspec == QUERY_STYLE_NOTHING)
4227     {
4228         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4229         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4230     }
4231     else
4232     {
4233         sp_desktop_set_style (desktop, css, true, true);
4234     }
4236     sp_style_unref(query);
4238     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4239                                    _("Text: Change font family"));
4240     sp_repr_css_attr_unref (css);
4241     free (family);
4242     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4244     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4247 void
4248 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4249                                        GObject      *tbl)
4251     const char *family = gtk_entry_get_text (entry);
4253     try {
4254         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4255         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4256         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4257         gtk_tree_selection_select_path (selection, path.gobj());
4258         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4259         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4260     } catch (...) {
4261         if (family && strlen (family))
4262         {
4263             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4264         }
4265     }
4268 void
4269 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4270                                    gpointer          data)
4272     if (g_object_get_data (G_OBJECT (button), "block")) return;
4273     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4274     int prop = GPOINTER_TO_INT(data);
4276     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4277     SPCSSAttr *css = sp_repr_css_attr_new ();
4279     switch (prop)
4280     {
4281         case 0:
4282         {
4283             sp_repr_css_set_property (css, "text-anchor", "start");
4284             sp_repr_css_set_property (css, "text-align", "start");
4285             break;
4286         }
4287         case 1:
4288         {
4289             sp_repr_css_set_property (css, "text-anchor", "middle");
4290             sp_repr_css_set_property (css, "text-align", "center");
4291             break;
4292         }
4294         case 2:
4295         {
4296             sp_repr_css_set_property (css, "text-anchor", "end");
4297             sp_repr_css_set_property (css, "text-align", "end");
4298             break;
4299         }
4301         case 3:
4302         {
4303             sp_repr_css_set_property (css, "text-anchor", "start");
4304             sp_repr_css_set_property (css, "text-align", "justify");
4305             break;
4306         }
4307     }
4309     SPStyle *query =
4310         sp_style_new (SP_ACTIVE_DOCUMENT);
4311     int result_numbers =
4312         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4314     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4315     if (result_numbers == QUERY_STYLE_NOTHING)
4316     {
4317         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4318     }
4320     sp_style_unref(query);
4322     sp_desktop_set_style (desktop, css, true, true);
4323     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4324                                    _("Text: Change alignment"));
4325     sp_repr_css_attr_unref (css);
4327     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4330 void
4331 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4332                                gpointer          data)
4334     if (g_object_get_data (G_OBJECT (button), "block")) return;
4336     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4337     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4338     int          prop       = GPOINTER_TO_INT(data);
4339     bool         active     = gtk_toggle_button_get_active (button);
4341     SPStyle *query =
4342         sp_style_new (SP_ACTIVE_DOCUMENT);
4343     
4344     int result_fontspec =
4345         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4347     int result_family =
4348         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4350     int result_style =
4351         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4353     int result_numbers =
4354         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4355     
4356     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4357     Glib::ustring newFontSpec = "";
4358     
4359     if (fontSpec.empty()) {
4360         // Construct a new font specification if it does not yet exist
4361         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4362         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4363         fontFromStyle->Unref();
4364     }
4365     
4366     switch (prop)
4367     {
4368         case 0:
4369         {
4370             if (!fontSpec.empty()) {
4371                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4372             }
4373             if (fontSpec != newFontSpec) {
4374                 // Don't even set the bold if the font didn't exist on the system
4375                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4376             }
4377             break;
4378         }
4380         case 1:
4381         {
4382             if (!fontSpec.empty()) {
4383                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4384             }
4385             if (fontSpec != newFontSpec) {
4386                 // Don't even set the italic if the font didn't exist on the system
4387                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4388             }
4389             break;
4390         }
4391     }
4393     if (!newFontSpec.empty()) {
4394         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str()); 
4395     }
4397     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4398     if (result_fontspec == QUERY_STYLE_NOTHING)
4399     {
4400         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4401     }
4403     sp_style_unref(query);
4405     sp_desktop_set_style (desktop, css, true, true);
4406     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4407                                    _("Text: Change font style"));
4408     sp_repr_css_attr_unref (css);
4410     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4413 void
4414 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4415                                      gpointer         data)
4417     if (g_object_get_data (G_OBJECT (button), "block")) {
4418         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4419         return;
4420     }
4422     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4423     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4424     int          prop       = GPOINTER_TO_INT(data);
4426     switch (prop)
4427     {
4428         case 0:
4429         {
4430             sp_repr_css_set_property (css, "writing-mode", "lr");
4431             break;
4432         }
4434         case 1:
4435         {
4436             sp_repr_css_set_property (css, "writing-mode", "tb");
4437             break;
4438         }
4439     }
4441     SPStyle *query =
4442         sp_style_new (SP_ACTIVE_DOCUMENT);
4443     int result_numbers =
4444         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4446     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4447     if (result_numbers == QUERY_STYLE_NOTHING)
4448     {
4449         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4450     }
4452     sp_desktop_set_style (desktop, css, true, true);
4453     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4454                                    _("Text: Change orientation"));
4455     sp_repr_css_attr_unref (css);
4457     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4460 gboolean
4461 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4463     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4464     if (!desktop) return FALSE;
4466     switch (get_group0_keyval (event)) {
4467         case GDK_Escape: // defocus
4468             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4469             sp_text_toolbox_selection_changed (NULL, tbl); // update
4470             return TRUE; // I consumed the event
4471             break;
4472     }
4473     return FALSE;
4476 gboolean
4477 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4479     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4480     if (!desktop) return FALSE;
4482     switch (get_group0_keyval (event)) {
4483         case GDK_KP_Enter:
4484         case GDK_Return:
4485         case GDK_Escape: // defocus
4486             gtk_widget_hide (w);
4487             popdown_visible = false;
4488             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4489             return TRUE; // I consumed the event
4490             break;
4491         case GDK_w: 
4492         case GDK_W: 
4493             if (event->state & GDK_CONTROL_MASK) {
4494                 gtk_widget_hide (w);
4495                 popdown_visible = false;
4496                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4497                 return TRUE; // I consumed the event
4498             }
4499             break;
4500     }
4501     return FALSE;
4505 void
4506 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4507                                GObject     *tbl)
4509     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4511     if (g_object_get_data (tbl, "size-block")) return;
4513     // If this is not from selecting a size in the list (in which case get_active will give the
4514     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4515     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4516     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4517     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4518         return;
4520     gchar *endptr;
4521     gdouble value = -1;
4522     char *text = gtk_combo_box_get_active_text (cbox);
4523     if (text) {
4524         value = g_strtod (text, &endptr);
4525         if (endptr == text) // conversion failed, non-numeric input
4526             value = -1;
4527         free (text);
4528     }
4529     if (value <= 0) {
4530         return; // could not parse value 
4531     }
4533     SPCSSAttr *css = sp_repr_css_attr_new ();
4534     Inkscape::CSSOStringStream osfs;
4535     osfs << value;
4536     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4538     SPStyle *query =
4539         sp_style_new (SP_ACTIVE_DOCUMENT);
4540     int result_numbers =
4541         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4543     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4544     if (result_numbers == QUERY_STYLE_NOTHING)
4545     {
4546         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4547     }
4549     sp_style_unref(query);
4551     sp_desktop_set_style (desktop, css, true, true);
4552     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4553                                    _("Text: Change font size"));
4554     sp_repr_css_attr_unref (css);
4556     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4559 gboolean
4560 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus *event, GObject *tbl)
4562     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4563     if (!desktop) return FALSE;
4565     if (!g_object_get_data (tbl, "esc-pressed")) {
4566         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4567         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4568         sp_text_toolbox_size_changed (cbox, tbl);
4569         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4570     }
4571     return FALSE; // I consumed the event
4575 gboolean
4576 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4578     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4579     if (!desktop) return FALSE;
4581     switch (get_group0_keyval (event)) {
4582         case GDK_Escape: // defocus
4583             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4584             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4585             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4586             return TRUE; // I consumed the event
4587             break;
4588         case GDK_Return: // defocus
4589         case GDK_KP_Enter:
4590             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4591             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4592             sp_text_toolbox_size_changed (cbox, tbl);
4593             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4594             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4595             return TRUE; // I consumed the event
4596             break;
4597     }
4598     return FALSE;
4601 void
4602 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4603                                          GObject            *tbl)
4605     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4606     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4607     int x, y;
4609     if (!popdown_visible)
4610     {
4611         gdk_window_get_origin (widget->window, &x, &y);
4612         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4613         gtk_widget_show_all (popdown);
4614         //sp_transientize (popdown);
4616         gdk_pointer_grab (widget->window, TRUE,
4617                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4618                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4619                                         GDK_POINTER_MOTION_MASK),
4620                           NULL, NULL, GDK_CURRENT_TIME);
4622         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4624         popdown_visible = true;
4625     }
4626     else
4627     {
4628         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4629         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4630         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4631         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4632         gtk_widget_hide (popdown);
4633         popdown_visible = false;
4634     }
4637 gboolean
4638 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4639                                  GdkEventFocus    */*event*/,
4640                                  GObject          */*tbl*/)
4642     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4643     return FALSE;
4646 gboolean
4647 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4648                                    GdkEventFocus    */*event*/,
4649                                    GObject          */*tbl*/)
4651     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4653     if (popdown_hasfocus) {
4654         gtk_widget_hide (popdown);
4655         popdown_hasfocus = false;
4656         popdown_visible = false;
4657         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4658         return TRUE;
4659     }
4660     return FALSE;
4663 gboolean
4664 sp_text_toolbox_popdown_focus_in (GtkWidget        *popdown,
4665                                    GdkEventFocus    */*event*/,
4666                                    GObject          */*tbl*/)
4668     popdown_hasfocus = true;
4669     return TRUE;
4673 void
4674 cell_data_func  (GtkTreeViewColumn */*column*/,
4675                  GtkCellRenderer   *cell,
4676                  GtkTreeModel      *tree_model,
4677                  GtkTreeIter       *iter,
4678                  gpointer           /*data*/)
4680     char        *family,
4681         *family_escaped,
4682         *sample_escaped;
4684     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4686     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4688     family_escaped = g_markup_escape_text (family, -1);
4689     sample_escaped = g_markup_escape_text (sample, -1);
4691     std::stringstream markup;
4692     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4693     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4695     free (family);
4696     free (family_escaped);
4697     free (sample_escaped);
4700 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4701     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4702     if (completion) {
4703         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4704         g_object_unref (completion);
4705     }
4708 GtkWidget*
4709 sp_text_toolbox_new (SPDesktop *desktop)
4711     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4713     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4714     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4716     GtkTooltips *tt = gtk_tooltips_new();
4717     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4719     ////////////Family
4720     //Window
4721     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4722     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4724     //Entry
4725     GtkWidget           *entry = gtk_entry_new ();
4726     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4727     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4728     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4729     gtk_entry_completion_set_text_column (completion, 0);
4730     gtk_entry_completion_set_minimum_key_length (completion, 1);
4731     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4732     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4733     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4734     aux_toolbox_space (tbl, 1);
4735     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4736     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4738     //Button
4739     GtkWidget   *button = gtk_button_new ();
4740     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4741     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4743     //Popdown
4744     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4745     GtkWidget           *treeview = gtk_tree_view_new ();
4747     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4748     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4749     gtk_tree_view_column_pack_start (column, cell, FALSE);
4750     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4751     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4752     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4754     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4755     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4756     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4758     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4760     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4761     gtk_container_add (GTK_CONTAINER (sw), treeview);
4763     gtk_container_add (GTK_CONTAINER (window), sw);
4764     gtk_widget_set_size_request (window, 300, 450);
4766     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4767     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4768     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4770     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4772     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4773     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
4774     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4776     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4777     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4779     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4780     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4781     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4782     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4783     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4785     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4786     aux_toolbox_space (tbl, 1);
4787     GtkWidget *box = gtk_event_box_new ();
4788     gtk_container_add (GTK_CONTAINER (box), image);
4789     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4790     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4791     GtkTooltips *tooltips = gtk_tooltips_new ();
4792     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4793     gtk_widget_hide (GTK_WIDGET (box));
4794     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4796     ////////////Size
4797     const char *sizes[] = {
4798         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4799         "16", "18", "20", "22", "24", "28",
4800         "32", "36", "40", "48", "56", "64", "72", "144"
4801     };
4803     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4804     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4805     gtk_widget_set_size_request (cbox, 80, -1);
4806     aux_toolbox_space (tbl, 1);
4807     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4808     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4809     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4810     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4811     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4813     //spacer
4814     aux_toolbox_space (tbl, 4);
4815     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4817     ////////////Text anchor
4818     GtkWidget *group   = gtk_radio_button_new (NULL);
4819     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4820     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4822     // left
4823     GtkWidget *rbutton = group;
4824     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4825     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4826     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4828     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4829     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4830     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4831     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4833     // center
4834     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4835     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4836     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4837     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4839     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4840     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4841     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4842     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4844     // right
4845     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4846     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4847     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4848     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4850     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4851     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4852     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4853     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4855     // fill
4856     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4857     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4858     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4859     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4861     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4862     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4863     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4864     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4866     aux_toolbox_space (tbl, 1);
4867     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4869     //spacer
4870     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4872     ////////////Text style
4873     row = gtk_hbox_new (FALSE, 4);
4875     // bold
4876     rbutton = gtk_toggle_button_new ();
4877     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4878     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4879     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4880     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4882     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4883     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4884     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4886     // italic
4887     rbutton = gtk_toggle_button_new ();
4888     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4889     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4890     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4891     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4893     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4894     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4895     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4897     aux_toolbox_space (tbl, 1);
4898     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4900     //spacer
4901     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4903     ////////////Text orientation
4904     group   = gtk_radio_button_new (NULL);
4905     row     = gtk_hbox_new (FALSE, 4);
4906     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4908     // horizontal
4909     rbutton = group;
4910     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4911     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4912     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4913     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4915     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4916     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4917     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4919     // vertical
4920     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4921     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4922     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4923     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4924     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4926     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4927     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4928     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4929     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4932     //watch selection
4933     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4935     sigc::connection *c_selection_changed =
4936         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4937                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4938     pool->add_connection ("selection-changed", c_selection_changed);
4940     sigc::connection *c_selection_modified =
4941         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4942                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4943     pool->add_connection ("selection-modified", c_selection_modified);
4945     sigc::connection *c_subselection_changed =
4946         new sigc::connection (desktop->connectToolSubselectionChanged
4947                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4948     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4950     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4953     gtk_widget_show_all (tbl);
4954     return tbl;
4956 } // end of sp_text_toolbox_new()
4958 }//<unnamed> namespace
4961 //#########################
4962 //##      Connector      ##
4963 //#########################
4965 static void sp_connector_path_set_avoid(void)
4967     cc_selection_set_avoid(true);
4971 static void sp_connector_path_set_ignore(void)
4973     cc_selection_set_avoid(false);
4978 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4980     // quit if run by the _changed callbacks
4981     if (g_object_get_data( tbl, "freeze" )) {
4982         return;
4983     }
4985     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4986     SPDocument *doc = sp_desktop_document(desktop);
4988     if (!sp_document_get_undo_sensitive(doc))
4989     {
4990         return;
4991     }
4993     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4995     if ( repr->attribute("inkscape:connector-spacing") ) {
4996         gdouble priorValue = gtk_adjustment_get_value(adj);
4997         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4998         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4999             return;
5000         }
5001     } else if ( adj->value == defaultConnSpacing ) {
5002         return;
5003     }
5005     // in turn, prevent callbacks from responding
5006     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5008     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5009     SP_OBJECT(desktop->namedview)->updateRepr();
5011     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5012     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5013         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5014         NR::Matrix m = NR::identity();
5015         avoid_item_move(&m, item);
5016     }
5018     if (items) {
5019         g_slist_free(items);
5020     }
5022     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5023             _("Change connector spacing"));
5025     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5027     spinbutton_defocus(GTK_OBJECT(tbl));
5030 static void sp_connector_graph_layout(void)
5032     if (!SP_ACTIVE_DESKTOP) return;
5034     // hack for clones, see comment in align-and-distribute.cpp
5035     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5036     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5038     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5040     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5042     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5045 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5047     if ( gtk_toggle_action_get_active( act ) ) {
5048         prefs_set_string_attribute("tools.connector", "directedlayout",
5049                 "true");
5050     } else {
5051         prefs_set_string_attribute("tools.connector", "directedlayout",
5052                 "false");
5053     }
5056 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5058     if ( gtk_toggle_action_get_active( act ) ) {
5059         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5060                 "true");
5061     } else {
5062         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5063                 "false");
5064     }
5068 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5070     prefs_set_double_attribute("tools.connector", "length", adj->value);
5071     spinbutton_defocus(GTK_OBJECT(tbl));
5074 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5075                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5076                                             bool /*is_interactive*/, gpointer data)
5078     GtkWidget *tbl = GTK_WIDGET(data);
5080     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5081         return;
5082     }
5083     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5084         return;
5085     }
5087     GtkAdjustment *adj = (GtkAdjustment*)
5088             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5089     gdouble spacing = defaultConnSpacing;
5090     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5092     gtk_adjustment_set_value(adj, spacing);
5096 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5097     NULL, /* child_added */
5098     NULL, /* child_removed */
5099     connector_tb_event_attr_changed,
5100     NULL, /* content_changed */
5101     NULL  /* order_changed */
5102 };
5105 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5107     {
5108         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5109                                           _("Avoid"),
5110                                           _("Make connectors avoid selected objects"),
5111                                           "connector_avoid",
5112                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5113         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5114         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5115     }
5117     {
5118         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5119                                           _("Ignore"),
5120                                           _("Make connectors ignore selected objects"),
5121                                           "connector_ignore",
5122                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5123         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5124         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5125     }
5127     EgeAdjustmentAction* eact = 0;
5129     // Spacing spinbox
5130     eact = create_adjustment_action( "ConnectorSpacingAction",
5131                                      _("Connector Spacing"), _("Spacing:"),
5132                                      _("The amount of space left around objects by auto-routing connectors"),
5133                                      "tools.connector", "spacing", defaultConnSpacing,
5134                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5135                                      0, 100, 1.0, 10.0,
5136                                      0, 0, 0,
5137                                      connector_spacing_changed, 1, 0 );
5138     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5140     // Graph (connector network) layout
5141     {
5142         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5143                                           _("Graph"),
5144                                           _("Nicely arrange selected connector network"),
5145                                           "graph_layout",
5146                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5147         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5148         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5149     }
5151     // Default connector length spinbox
5152     eact = create_adjustment_action( "ConnectorLengthAction",
5153                                      _("Connector Length"), _("Length:"),
5154                                      _("Ideal length for connectors when layout is applied"),
5155                                      "tools.connector", "length", 100,
5156                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5157                                      10, 1000, 10.0, 100.0,
5158                                      0, 0, 0,
5159                                      connector_length_changed, 1, 0 );
5160     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5163     // Directed edges toggle button
5164     {
5165         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5166                                                       _("Downwards"),
5167                                                       _("Make connectors with end-markers (arrows) point downwards"),
5168                                                       "directed_graph",
5169                                                       Inkscape::ICON_SIZE_DECORATION );
5170         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5172         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5173         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5174                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5176         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5177     }
5179     // Avoid overlaps toggle button
5180     {
5181         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5182                                                       _("Remove overlaps"),
5183                                                       _("Do not allow overlapping shapes"),
5184                                                       "remove_overlaps",
5185                                                       Inkscape::ICON_SIZE_DECORATION );
5186         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5188         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5189         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5190                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5192         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5193     }
5195     // Code to watch for changes to the connector-spacing attribute in
5196     // the XML.
5197     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5198     g_assert(repr != NULL);
5200     purge_repr_listener( holder, holder );
5202     if (repr) {
5203         g_object_set_data( holder, "repr", repr );
5204         Inkscape::GC::anchor(repr);
5205         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5206         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5207     }
5208 } // end of sp_connector_toolbox_prep()
5211 //#########################
5212 //##     Paintbucket     ##
5213 //#########################
5215 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5217     gint channels = ege_select_one_action_get_active( act );
5218     flood_channels_set_channels( channels );
5221 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5223     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5226 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5228     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5231 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5233     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5234     SPUnit const *unit = tracker->getActiveUnit();
5236     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5238     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5241 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5243     // FIXME: make defaults settable via Inkscape Options
5244     struct KeyValue {
5245         char const *key;
5246         double value;
5247     } const key_values[] = {
5248         {"threshold", 15},
5249         {"offset", 0.0}
5250     };
5252     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5253         KeyValue const &kv = key_values[i];
5254         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5255         if ( adj ) {
5256             gtk_adjustment_set_value(adj, kv.value);
5257         }
5258     }
5260     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5261     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5262     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5263     ege_select_one_action_set_active( autogap_action, 0 );
5266 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5268     EgeAdjustmentAction* eact = 0;
5270     {
5271         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5273         GList* items = 0;
5274         gint count = 0;
5275         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5276         {
5277             GtkTreeIter iter;
5278             gtk_list_store_append( model, &iter );
5279             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5280             count++;
5281         }
5282         g_list_free( items );
5283         items = 0;
5284         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5285         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5286         ege_select_one_action_set_appearance( act1, "compact" );
5287         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5288         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5289         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5290         g_object_set_data( holder, "channels_action", act1 );
5291     }
5293     // Spacing spinbox
5294     {
5295         eact = create_adjustment_action(
5296             "ThresholdAction",
5297             _("Fill Threshold"), _("Threshold:"),
5298             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5299             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5300             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5301             0, 0, 0,
5302             paintbucket_threshold_changed, 1, 0 );
5304         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5305     }
5307     // Create the units menu.
5308     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5309     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5310     if (stored_unit)
5311         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5312     g_object_set_data( holder, "tracker", tracker );
5313     {
5314         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5315         gtk_action_group_add_action( mainActions, act );
5316     }
5318     // Offset spinbox
5319     {
5320         eact = create_adjustment_action(
5321             "OffsetAction",
5322             _("Grow/shrink by"), _("Grow/shrink by:"),
5323             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5324             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5325             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5326             0, 0, 0,
5327             paintbucket_offset_changed, 1, 2);
5328         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5330         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5331     }
5333     /* Auto Gap */
5334     {
5335         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5337         GList* items = 0;
5338         gint count = 0;
5339         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5340         {
5341             GtkTreeIter iter;
5342             gtk_list_store_append( model, &iter );
5343             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5344             count++;
5345         }
5346         g_list_free( items );
5347         items = 0;
5348         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5349         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5350         ege_select_one_action_set_appearance( act2, "compact" );
5351         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5352         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5353         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5354         g_object_set_data( holder, "autogap_action", act2 );
5355     }
5357     /* Reset */
5358     {
5359         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5360                                           _("Defaults"),
5361                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5362                                           GTK_STOCK_CLEAR );
5363         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5364         gtk_action_group_add_action( mainActions, act );
5365         gtk_action_set_sensitive( act, TRUE );
5366     }
5370 /*
5371   Local Variables:
5372   mode:c++
5373   c-file-style:"stroustrup"
5374   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5375   indent-tabs-mode:nil
5376   fill-column:99
5377   End:
5378 */
5379 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :