Code

Warning cleanup
[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         "    <toolitem action='NodesShowHelperpath' />"
254         "    <separator />"
255         "    <toolitem action='EditNextLPEParameterAction' />"
256         "    <separator />"
257         "    <toolitem action='NodeXAction' />"
258         "    <toolitem action='NodeYAction' />"
259         "    <toolitem action='NodeUnitsAction' />"
260         "  </toolbar>"
262         "  <toolbar name='TweakToolbar'>"
263         "    <toolitem action='TweakWidthAction' />"
264         "    <separator />"
265         "    <toolitem action='TweakForceAction' />"
266         "    <toolitem action='TweakPressureAction' />"
267         "    <separator />"
268         "    <toolitem action='TweakModeAction' />"
269         "    <separator />"
270         "    <toolitem action='TweakFidelityAction' />"
271         "    <separator />"
272         "    <toolitem action='TweakChannelsLabel' />"
273         "    <toolitem action='TweakDoH' />"
274         "    <toolitem action='TweakDoS' />"
275         "    <toolitem action='TweakDoL' />"
276         "    <toolitem action='TweakDoO' />"
277         "  </toolbar>"
279         "  <toolbar name='ZoomToolbar'>"
280         "    <toolitem action='ZoomIn' />"
281         "    <toolitem action='ZoomOut' />"
282         "    <separator />"
283         "    <toolitem action='Zoom1:0' />"
284         "    <toolitem action='Zoom1:2' />"
285         "    <toolitem action='Zoom2:1' />"
286         "    <separator />"
287         "    <toolitem action='ZoomSelection' />"
288         "    <toolitem action='ZoomDrawing' />"
289         "    <toolitem action='ZoomPage' />"
290         "    <toolitem action='ZoomPageWidth' />"
291         "    <separator />"
292         "    <toolitem action='ZoomPrev' />"
293         "    <toolitem action='ZoomNext' />"
294         "  </toolbar>"
296         "  <toolbar name='StarToolbar'>"
297         "    <separator />"
298         "    <toolitem action='StarStateAction' />"
299         "    <separator />"
300         "    <toolitem action='FlatAction' />"
301         "    <separator />"
302         "    <toolitem action='MagnitudeAction' />"
303         "    <toolitem action='SpokeAction' />"
304         "    <toolitem action='RoundednessAction' />"
305         "    <toolitem action='RandomizationAction' />"
306         "    <separator />"
307         "    <toolitem action='StarResetAction' />"
308         "  </toolbar>"
310         "  <toolbar name='RectToolbar'>"
311         "    <toolitem action='RectStateAction' />"
312         "    <toolitem action='RectWidthAction' />"
313         "    <toolitem action='RectHeightAction' />"
314         "    <toolitem action='RadiusXAction' />"
315         "    <toolitem action='RadiusYAction' />"
316         "    <toolitem action='RectUnitsAction' />"
317         "    <separator />"
318         "    <toolitem action='RectResetAction' />"
319         "  </toolbar>"
321         "  <toolbar name='3DBoxToolbar'>"
322         "    <toolitem action='3DBoxAngleXAction' />"
323         "    <toolitem action='3DBoxVPXStateAction' />"
324         "    <separator />"
325         "    <toolitem action='3DBoxAngleYAction' />"
326         "    <toolitem action='3DBoxVPYStateAction' />"
327         "    <separator />"
328         "    <toolitem action='3DBoxAngleZAction' />"
329         "    <toolitem action='3DBoxVPZStateAction' />"
330         "  </toolbar>"
332         "  <toolbar name='SpiralToolbar'>"
333         "    <toolitem action='SpiralStateAction' />"
334         "    <toolitem action='SpiralRevolutionAction' />"
335         "    <toolitem action='SpiralExpansionAction' />"
336         "    <toolitem action='SpiralT0Action' />"
337         "    <separator />"
338         "    <toolitem action='SpiralResetAction' />"
339         "  </toolbar>"
341         "  <toolbar name='PenToolbar'>"
342         "  </toolbar>"
344         "  <toolbar name='PencilToolbar'>"
345         "  </toolbar>"
347         "  <toolbar name='CalligraphyToolbar'>"
348         "    <separator />"
349         "    <toolitem action='CalligraphyWidthAction' />"
350         "    <toolitem action='PressureAction' />"
351         "    <toolitem action='TraceAction' />"
352         "    <toolitem action='ThinningAction' />"
353         "    <separator />"
354         "    <toolitem action='AngleAction' />"
355         "    <toolitem action='TiltAction' />"
356         "    <toolitem action='FixationAction' />"
357         "    <separator />"
358         "    <toolitem action='CapRoundingAction' />"
359         "    <separator />"
360         "    <toolitem action='TremorAction' />"
361         "    <toolitem action='WiggleAction' />"
362         "    <toolitem action='MassAction' />"
363         "    <separator />"
364         "    <toolitem action='CalligraphyResetAction' />"
365         "  </toolbar>"
367         "  <toolbar name='ArcToolbar'>"
368         "    <toolitem action='ArcStateAction' />"
369         "    <separator />"
370         "    <toolitem action='ArcStartAction' />"
371         "    <toolitem action='ArcEndAction' />"
372         "    <separator />"
373         "    <toolitem action='ArcOpenAction' />"
374         "    <separator />"
375         "    <toolitem action='ArcResetAction' />"
376         "    <separator />"
377         "  </toolbar>"
379         "  <toolbar name='PaintbucketToolbar'>"
380         "    <toolitem action='ChannelsAction' />"
381         "    <separator />"
382         "    <toolitem action='ThresholdAction' />"
383         "    <separator />"
384         "    <toolitem action='OffsetAction' />"
385         "    <toolitem action='PaintbucketUnitsAction' />"
386         "    <separator />"
387         "    <toolitem action='AutoGapAction' />"
388         "    <separator />"
389         "    <toolitem action='PaintbucketResetAction' />"
390         "  </toolbar>"
392         "  <toolbar name='DropperToolbar'>"
393         "    <toolitem action='DropperPickAlphaAction' />"
394         "    <toolitem action='DropperSetAlphaAction' />"
395         "  </toolbar>"
397         "  <toolbar name='ConnectorToolbar'>"
398         "    <toolitem action='ConnectorAvoidAction' />"
399         "    <toolitem action='ConnectorIgnoreAction' />"
400         "    <toolitem action='ConnectorSpacingAction' />"
401         "    <toolitem action='ConnectorGraphAction' />"
402         "    <toolitem action='ConnectorLengthAction' />"
403         "    <toolitem action='ConnectorDirectedAction' />"
404         "    <toolitem action='ConnectorOverlapAction' />"
405         "  </toolbar>"
407         "</ui>"
410 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
412 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
414 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
415 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
417 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
418 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
420 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
421 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
423 /* Global text entry widgets necessary for update */
424 /* GtkWidget *dropper_rgb_entry,
425           *dropper_opacity_entry ; */
426 // should be made a private member once this is converted to class
428 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
429     connection->disconnect();
430     delete connection;
433 static void purge_repr_listener( GObject* obj, GObject* tbl )
435     (void)obj;
436     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
437     if (oldrepr) { // remove old listener
438         sp_repr_remove_listener_by_data(oldrepr, tbl);
439         Inkscape::GC::release(oldrepr);
440         oldrepr = 0;
441         g_object_set_data( tbl, "repr", NULL );
442     }
445 GtkWidget *
446 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
447                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
448                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
450     SPAction *action = verb->get_action(view);
451     if (!action) return NULL;
453     SPAction *doubleclick_action;
454     if (doubleclick_verb)
455         doubleclick_action = doubleclick_verb->get_action(view);
456     else
457         doubleclick_action = NULL;
459     /* fixme: Handle sensitive/unsensitive */
460     /* fixme: Implement sp_button_new_from_action */
461     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
462     gtk_widget_show(b);
463     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
465     return b;
468 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
469                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
471     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
474 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
475                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
477     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
481 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
483     SPAction* targetAction = SP_ACTION(user_data);
484     if ( targetAction ) {
485         sp_action_perform( targetAction, NULL );
486     }
489 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
491     if ( data ) {
492         GtkAction* act = GTK_ACTION(data);
493         gtk_action_set_sensitive( act, sensitive );
494     }
497 static SPActionEventVector action_event_vector = {
498     {NULL},
499     NULL,
500     NULL,
501     sp_action_action_set_sensitive,
502     NULL,
503     NULL
504 };
506 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
508     GtkAction* act = 0;
510     SPAction* targetAction = verb->get_action(view);
511     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
512     act = GTK_ACTION(inky);
513     gtk_action_set_sensitive( act, targetAction->sensitive );
515     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
517     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
518     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
520     return act;
523 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
525     Inkscape::UI::View::View *view = desktop;
526     gint verbsToUse[] = {
527         // disabled until we have icons for them:
528         //find
529         //SP_VERB_EDIT_TILE,
530         //SP_VERB_EDIT_UNTILE,
531         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
532         SP_VERB_DIALOG_DISPLAY,
533         SP_VERB_DIALOG_FILL_STROKE,
534         SP_VERB_DIALOG_NAMEDVIEW,
535         SP_VERB_DIALOG_TEXT,
536         SP_VERB_DIALOG_XML_EDITOR,
537         SP_VERB_EDIT_CLONE,
538         SP_VERB_EDIT_COPY,
539         SP_VERB_EDIT_CUT,
540         SP_VERB_EDIT_DUPLICATE,
541         SP_VERB_EDIT_PASTE,
542         SP_VERB_EDIT_REDO,
543         SP_VERB_EDIT_UNDO,
544         SP_VERB_EDIT_UNLINK_CLONE,
545         SP_VERB_FILE_EXPORT,
546         SP_VERB_FILE_IMPORT,
547         SP_VERB_FILE_NEW,
548         SP_VERB_FILE_OPEN,
549         SP_VERB_FILE_PRINT,
550         SP_VERB_FILE_SAVE,
551         SP_VERB_OBJECT_TO_CURVE,
552         SP_VERB_SELECTION_GROUP,
553         SP_VERB_SELECTION_OUTLINE,
554         SP_VERB_SELECTION_UNGROUP,
555         SP_VERB_ZOOM_1_1,
556         SP_VERB_ZOOM_1_2,
557         SP_VERB_ZOOM_2_1,
558         SP_VERB_ZOOM_DRAWING,
559         SP_VERB_ZOOM_IN,
560         SP_VERB_ZOOM_NEXT,
561         SP_VERB_ZOOM_OUT,
562         SP_VERB_ZOOM_PAGE,
563         SP_VERB_ZOOM_PAGE_WIDTH,
564         SP_VERB_ZOOM_PREV,
565         SP_VERB_ZOOM_SELECTION,
566     };
568     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
569     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
571     static std::map<SPDesktop*, GtkActionGroup*> groups;
572     GtkActionGroup* mainActions = 0;
573     if ( groups.find(desktop) != groups.end() ) {
574         mainActions = groups[desktop];
575     }
577     if ( !mainActions ) {
578         mainActions = gtk_action_group_new("main");
579         groups[desktop] = mainActions;
580     }
582     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
583         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
584         if ( verb ) {
585             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
586                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
587                 gtk_action_group_add_action( mainActions, act );
588             }
589         }
590     }
592     return mainActions;
596 GtkWidget *
597 sp_tool_toolbox_new()
599     GtkTooltips *tt = gtk_tooltips_new();
600     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
602     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
603     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
605     gtk_widget_set_sensitive(tb, FALSE);
607     GtkWidget *hb = gtk_handle_box_new();
608     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
609     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
610     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
612     gtk_container_add(GTK_CONTAINER(hb), tb);
613     gtk_widget_show(GTK_WIDGET(tb));
615     sigc::connection* conn = new sigc::connection;
616     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
618     return hb;
621 static void
622 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
624     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
625     gtk_widget_queue_resize(child);
628 static void
629 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
631     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
632     gtk_widget_queue_resize(child);
635 GtkWidget *
636 sp_aux_toolbox_new()
638     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
640     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
642     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
644     gtk_widget_set_sensitive(tb, FALSE);
646     GtkWidget *hb = gtk_handle_box_new();
647     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
648     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
649     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
651     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
652     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
654     gtk_container_add(GTK_CONTAINER(hb), tb);
655     gtk_widget_show(GTK_WIDGET(tb));
657     sigc::connection* conn = new sigc::connection;
658     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
660     return hb;
663 //####################################
664 //# Commands Bar
665 //####################################
667 GtkWidget *
668 sp_commands_toolbox_new()
670     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
672     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
674     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
675     gtk_widget_set_sensitive(tb, FALSE);
677     GtkWidget *hb = gtk_handle_box_new();
678     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
679     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
680     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
682     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
683     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
685     gtk_container_add(GTK_CONTAINER(hb), tb);
686     gtk_widget_show(GTK_WIDGET(tb));
688     sigc::connection* conn = new sigc::connection;
689     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
691     return hb;
694 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
695                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
696                                                        gchar const *path, gchar const *data, gdouble def,
697                                                        GtkWidget *focusTarget,
698                                                        GtkWidget *us,
699                                                        GObject *dataKludge,
700                                                        gboolean altx, gchar const *altx_mark,
701                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
702                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
703                                                        void (*callback)(GtkAdjustment *, GObject *),
704                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
706     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
707                                                              lower, upper, step, page, page ) );
708     if (us) {
709         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
710     }
712     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
714     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
715     if ( shortLabel ) {
716         g_object_set( act, "short_label", shortLabel, NULL );
717     }
719     if ( (descrCount > 0) && descrLabels && descrValues ) {
720         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
721     }
723     if ( focusTarget ) {
724         ege_adjustment_action_set_focuswidget( act, focusTarget );
725     }
727     if ( altx && altx_mark ) {
728         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
729     }
731     if ( dataKludge ) {
732         g_object_set_data( dataKludge, data, adj );
733     }
735     // Using a cast just to make sure we pass in the right kind of function pointer
736     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
738     return act;
742 //####################################
743 //# node editing callbacks
744 //####################################
746 /**
747  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
748  */
749 static ShapeEditor *get_current_shape_editor()
751     if (!SP_ACTIVE_DESKTOP) {
752         return NULL;
753     }
755     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
757     if (!SP_IS_NODE_CONTEXT(event_context)) {
758         return NULL;
759     }
761     return SP_NODE_CONTEXT(event_context)->shape_editor;
765 void
766 sp_node_path_edit_add(void)
768     ShapeEditor *shape_editor = get_current_shape_editor();
769     if (shape_editor) shape_editor->add_node();
772 void
773 sp_node_path_edit_delete(void)
775     ShapeEditor *shape_editor = get_current_shape_editor();
776     if (shape_editor) shape_editor->delete_nodes();
779 void
780 sp_node_path_edit_delete_segment(void)
782     ShapeEditor *shape_editor = get_current_shape_editor();
783     if (shape_editor) shape_editor->delete_segment();
786 void
787 sp_node_path_edit_break(void)
789     ShapeEditor *shape_editor = get_current_shape_editor();
790     if (shape_editor) shape_editor->break_at_nodes();
793 void
794 sp_node_path_edit_join(void)
796     ShapeEditor *shape_editor = get_current_shape_editor();
797     if (shape_editor) shape_editor->join_nodes();
800 void
801 sp_node_path_edit_join_segment(void)
803     ShapeEditor *shape_editor = get_current_shape_editor();
804     if (shape_editor) shape_editor->join_segments();
807 void
808 sp_node_path_edit_toline(void)
810     ShapeEditor *shape_editor = get_current_shape_editor();
811     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
814 void
815 sp_node_path_edit_tocurve(void)
817     ShapeEditor *shape_editor = get_current_shape_editor();
818     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
821 void
822 sp_node_path_edit_cusp(void)
824     ShapeEditor *shape_editor = get_current_shape_editor();
825     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
828 void
829 sp_node_path_edit_smooth(void)
831     ShapeEditor *shape_editor = get_current_shape_editor();
832     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
835 void
836 sp_node_path_edit_symmetrical(void)
838     ShapeEditor *shape_editor = get_current_shape_editor();
839     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
842 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
843     bool show = gtk_toggle_action_get_active( act );
844     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
845     ShapeEditor *shape_editor = get_current_shape_editor();
846     if (shape_editor) shape_editor->show_handles(show);
849 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
850     bool show = gtk_toggle_action_get_active( act );
851     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
852     ShapeEditor *shape_editor = get_current_shape_editor();
853     if (shape_editor) shape_editor->show_helperpath(show);
856 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
857     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
860 /* is called when the node selection is modified */
861 static void
862 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
864     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
865     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
866     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
867     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
869     // quit if run by the attr_changed listener
870     if (g_object_get_data( tbl, "freeze" )) {
871         return;
872     }
874     // in turn, prevent listener from responding
875     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
877     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
878     SPUnit const *unit = tracker->getActiveUnit();
880     ShapeEditor *shape_editor = get_current_shape_editor();
881     if (shape_editor && shape_editor->has_nodepath()) {
882         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
883         int n_selected = 0;
884         if (nodepath) {
885             n_selected = nodepath->numSelected();
886         }
888         if (n_selected == 0) {
889             gtk_action_set_sensitive(xact, FALSE);
890             gtk_action_set_sensitive(yact, FALSE);
891         } else {
892             gtk_action_set_sensitive(xact, TRUE);
893             gtk_action_set_sensitive(yact, TRUE);
894             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
895             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
897             if (n_selected == 1) {
898                 NR::Point sel_node = nodepath->singleSelectedCoords();
899                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
900                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
901                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
902                 }
903             } else {
904                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
905                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
906                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
907                     /* Note: Currently x and y will always have a value, even if the coordinates of the
908                        selected nodes don't coincide (in this case we use the coordinates of the center
909                        of the bounding box). So the entries are never set to zero. */
910                     // FIXME: Maybe we should clear the entry if several nodes are selected
911                     //        instead of providing a kind of average value
912                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
913                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
914                 }
915             }
916         }
917     } else {
918         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
919         gtk_action_set_sensitive(xact, FALSE);
920         gtk_action_set_sensitive(yact, FALSE);
921     }
923     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
926 static void
927 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
929     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
931     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
932     SPUnit const *unit = tracker->getActiveUnit();
934     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
935         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
936     }
938     // quit if run by the attr_changed listener
939     if (g_object_get_data( tbl, "freeze" )) {
940         return;
941     }
943     // in turn, prevent listener from responding
944     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
946     ShapeEditor *shape_editor = get_current_shape_editor();
947     if (shape_editor && shape_editor->has_nodepath()) {
948         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
949         if (!strcmp(value_name, "x")) {
950             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
951         }
952         if (!strcmp(value_name, "y")) {
953             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
954         }
955     }
957     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
960 static void
961 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
963     sp_node_path_value_changed(adj, tbl, "x");
966 static void
967 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
969     sp_node_path_value_changed(adj, tbl, "y");
972 //################################
973 //##    Node Editing Toolbox    ##
974 //################################
976 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
978     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
979     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
980     g_object_set_data( holder, "tracker", tracker );
982     {
983         InkAction* inky = ink_action_new( "NodeInsertAction",
984                                           _("Insert node"),
985                                           _("Insert new nodes into selected segments"),
986                                           "node_insert",
987                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
988         g_object_set( inky, "short_label", _("Insert"), NULL );
989         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
990         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
991     }
993     {
994         InkAction* inky = ink_action_new( "NodeDeleteAction",
995                                           _("Delete node"),
996                                           _("Delete selected nodes"),
997                                           "node_delete",
998                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
999         g_object_set( inky, "short_label", _("Delete"), NULL );
1000         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1001         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1002     }
1004     {
1005         InkAction* inky = ink_action_new( "NodeJoinAction",
1006                                           _("Join endnodes"),
1007                                           _("Join selected endnodes"),
1008                                           "node_join",
1009                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1010         g_object_set( inky, "short_label", _("Join"), NULL );
1011         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1012         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1013     }
1015     {
1016         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1017                                           _("Join Segment"),
1018                                           _("Join selected endnodes with a new segment"),
1019                                           "node_join_segment",
1020                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1021         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1022         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1023     }
1025     {
1026         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1027                                           _("Delete Segment"),
1028                                           _("Split path between two non-endpoint nodes"),
1029                                           "node_delete_segment",
1030                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1031         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1032         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1033     }
1035     {
1036         InkAction* inky = ink_action_new( "NodeBreakAction",
1037                                           _("Node Break"),
1038                                           _("Break path at selected nodes"),
1039                                           "node_break",
1040                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1041         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1042         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1043     }
1045     {
1046         InkAction* inky = ink_action_new( "NodeCuspAction",
1047                                           _("Node Cusp"),
1048                                           _("Make selected nodes corner"),
1049                                           "node_cusp",
1050                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1051         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1052         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1053     }
1055     {
1056         InkAction* inky = ink_action_new( "NodeSmoothAction",
1057                                           _("Node Smooth"),
1058                                           _("Make selected nodes smooth"),
1059                                           "node_smooth",
1060                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1061         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1062         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1063     }
1065     {
1066         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1067                                           _("Node Symmetric"),
1068                                           _("Make selected nodes symmetric"),
1069                                           "node_symmetric",
1070                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1071         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1072         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1073     }
1075     {
1076         InkAction* inky = ink_action_new( "NodeLineAction",
1077                                           _("Node Line"),
1078                                           _("Make selected segments lines"),
1079                                           "node_line",
1080                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1081         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1082         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1083     }
1085     {
1086         InkAction* inky = ink_action_new( "NodeCurveAction",
1087                                           _("Node Curve"),
1088                                           _("Make selected segments curves"),
1089                                           "node_curve",
1090                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1091         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1092         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1093     }
1095     {
1096         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1097                                                       _("Show Handles"),
1098                                                       _("Show the Bezier handles of selected nodes"),
1099                                                       "nodes_show_handles",
1100                                                       Inkscape::ICON_SIZE_DECORATION );
1101         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1102         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1103         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1104     }
1106     {
1107         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1108                                                       _("Show Outline"),
1109                                                       _("Show the outline of the path"),
1110                                                       "nodes_show_helperpath",
1111                                                       Inkscape::ICON_SIZE_DECORATION );
1112         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1113         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1114         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1115     }
1117     {
1118         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1119                                           _("Next Path Effect Parameter"),
1120                                           _("Show next Path Effect parameter for editing"),
1121                                           "edit_next_parameter",
1122                                           Inkscape::ICON_SIZE_DECORATION );
1123         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1124         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1125     }
1127     /* X coord of selected node(s) */
1128     {
1129         EgeAdjustmentAction* eact = 0;
1130         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1131         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1132         eact = create_adjustment_action( "NodeXAction",
1133                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1134                                          "tools.nodes", "Xcoord", 0,
1135                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1136                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1137                                          labels, values, G_N_ELEMENTS(labels),
1138                                          sp_node_path_x_value_changed );
1139         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1140         g_object_set_data( holder, "nodes_x_action", eact );
1141         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1142         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1143     }
1145     /* Y coord of selected node(s) */
1146     {
1147         EgeAdjustmentAction* eact = 0;
1148         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1149         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1150         eact = create_adjustment_action( "NodeYAction",
1151                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1152                                          "tools.nodes", "Ycoord", 0,
1153                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1154                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1155                                          labels, values, G_N_ELEMENTS(labels),
1156                                          sp_node_path_y_value_changed );
1157         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1158         g_object_set_data( holder, "nodes_y_action", eact );
1159         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1160         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1161     }
1163     // add the units menu
1164     {
1165         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1166         gtk_action_group_add_action( mainActions, act );
1167     }
1169     sigc::connection *connection = new sigc::connection (
1170         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1171         );
1173     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1174     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1175 } // end of sp_node_toolbox_prep()
1178 //########################
1179 //##    Zoom Toolbox    ##
1180 //########################
1182 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1184     // no custom GtkAction setup needed
1185 } // end of sp_zoom_toolbox_prep()
1187 void
1188 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1190     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")));
1194 void
1195 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1197     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")));
1200 void
1201 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1203     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")));
1206 static void
1207 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1209     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1210     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1212     if (old_desktop) {
1213         GList *children, *iter;
1215         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1216         for ( iter = children ; iter ; iter = iter->next ) {
1217             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1218         }
1219         g_list_free(children);
1220     }
1222     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1224     if (desktop) {
1225         gtk_widget_set_sensitive(toolbox, TRUE);
1226         setup_func(toolbox, desktop);
1227         update_func(desktop, desktop->event_context, toolbox);
1228         *conn = desktop->connectEventContextChanged
1229             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1230     } else {
1231         gtk_widget_set_sensitive(toolbox, FALSE);
1232     }
1234 } // end of toolbox_set_desktop()
1237 static void
1238 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1240     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1241     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1242     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1243         // "toolbox.tools" was not set. Fallback to older value
1244         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1246         // Copy the setting forwards
1247         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1248     }
1249     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1251     for (int i = 0 ; tools[i].type_name ; i++ ) {
1252         GtkWidget *button =
1253             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1254                                                               SP_BUTTON_TYPE_TOGGLE,
1255                                                               Inkscape::Verb::get(tools[i].verb),
1256                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1257                                                               desktop,
1258                                                               tooltips );
1260         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1261                            (gpointer)button );
1262     }
1266 static void
1267 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1269     gchar const *const tname = ( eventcontext
1270                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1271                                  : NULL );
1272     for (int i = 0 ; tools[i].type_name ; i++ ) {
1273         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1274         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1275     }
1278 static void
1279 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1281     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1282     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1283     GtkUIManager* mgr = gtk_ui_manager_new();
1284     GError* errVal = 0;
1285     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1286     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1288     std::map<std::string, GtkWidget*> dataHolders;
1290     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1291         if ( aux_toolboxes[i].prep_func ) {
1292             // converted to GtkActions and UIManager
1294             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1295             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1296             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1297             dataHolders[aux_toolboxes[i].type_name] = kludge;
1298             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1299         } else {
1301             GtkWidget *sub_toolbox = 0;
1302             if (aux_toolboxes[i].create_func == NULL)
1303                 sub_toolbox = sp_empty_toolbox_new(desktop);
1304             else {
1305                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1306             }
1308             gtk_size_group_add_widget( grouper, sub_toolbox );
1310             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1311             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1313         }
1314     }
1316     // Second pass to create toolbars *after* all GtkActions are created
1317     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1318         if ( aux_toolboxes[i].prep_func ) {
1319             // converted to GtkActions and UIManager
1321             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1323             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1324             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1326             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1327             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1328             g_free( tmp );
1329             tmp = 0;
1331             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1332             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1333             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1334                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1335             }
1336             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1339             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1341             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1342                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1343                 swatch->setDesktop( desktop );
1344                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1345                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1346                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1347                 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 );
1348             }
1350             gtk_widget_show_all( holder );
1351             sp_set_font_size_smaller( holder );
1353             gtk_size_group_add_widget( grouper, holder );
1355             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1356             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1357         }
1358     }
1360     g_object_unref( G_OBJECT(grouper) );
1363 static void
1364 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1366     gchar const *tname = ( eventcontext
1367                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1368                            : NULL );
1369     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1370         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1371         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1372             gtk_widget_show_all(sub_toolbox);
1373             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1374         } else {
1375             gtk_widget_hide(sub_toolbox);
1376         }
1377     }
1380 static void
1381 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1383     gchar const * descr =
1384         "<ui>"
1385         "  <toolbar name='CommandsToolbar'>"
1386         "    <toolitem action='FileNew' />"
1387         "    <toolitem action='FileOpen' />"
1388         "    <toolitem action='FileSave' />"
1389         "    <toolitem action='FilePrint' />"
1390         "    <separator />"
1391         "    <toolitem action='FileImport' />"
1392         "    <toolitem action='FileExport' />"
1393         "    <separator />"
1394         "    <toolitem action='EditUndo' />"
1395         "    <toolitem action='EditRedo' />"
1396         "    <separator />"
1397         "    <toolitem action='EditCopy' />"
1398         "    <toolitem action='EditCut' />"
1399         "    <toolitem action='EditPaste' />"
1400         "    <separator />"
1401         "    <toolitem action='ZoomSelection' />"
1402         "    <toolitem action='ZoomDrawing' />"
1403         "    <toolitem action='ZoomPage' />"
1404         "    <separator />"
1405         "    <toolitem action='EditDuplicate' />"
1406         "    <toolitem action='EditClone' />"
1407         "    <toolitem action='EditUnlinkClone' />"
1408         "    <separator />"
1409         "    <toolitem action='SelectionGroup' />"
1410         "    <toolitem action='SelectionUnGroup' />"
1411         "    <separator />"
1412         "    <toolitem action='DialogFillStroke' />"
1413         "    <toolitem action='DialogText' />"
1414         "    <toolitem action='DialogXMLEditor' />"
1415         "    <toolitem action='DialogAlignDistribute' />"
1416         "    <separator />"
1417         "    <toolitem action='DialogPreferences' />"
1418         "    <toolitem action='DialogDocumentProperties' />"
1419         "  </toolbar>"
1420         "</ui>";
1421     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1424     GtkUIManager* mgr = gtk_ui_manager_new();
1425     GError* errVal = 0;
1427     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1428     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1430     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1431     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1432         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1433     }
1434     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1435     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1436     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1439     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1442 static void
1443 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1447 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1449     gtk_widget_show(toolbox_toplevel);
1450     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1452     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1453     if (!shown_toolbox) {
1454         return;
1455     }
1456     gtk_widget_show(toolbox);
1458     gtk_widget_show_all(shown_toolbox);
1461 void
1462 aux_toolbox_space(GtkWidget *tb, gint space)
1464     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1467 static GtkWidget *
1468 sp_empty_toolbox_new(SPDesktop *desktop)
1470     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1471     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1472     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1474     gtk_widget_show_all(tbl);
1475     sp_set_font_size_smaller (tbl);
1477     return tbl;
1480 // helper UI functions
1482 GtkWidget *
1483 sp_tb_spinbutton(
1484     gchar *label, gchar const *tooltip,
1485     gchar const *path, gchar const *data, gdouble def,
1486     GtkWidget *us,
1487     GtkWidget *tbl,
1488     gboolean altx, gchar const *altx_mark,
1489     gdouble lower, gdouble upper, gdouble step, gdouble page,
1490     void (*callback)(GtkAdjustment *, GtkWidget *),
1491     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1493     GtkTooltips *tt = gtk_tooltips_new();
1495     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1497     GtkWidget *l = gtk_label_new(label);
1498     gtk_widget_show(l);
1499     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1500     gtk_container_add(GTK_CONTAINER(hb), l);
1502     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1503                                       lower, upper, step, page, page);
1504     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1505     if (us)
1506         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1508     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1509     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1510     if (altx)
1511         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1512     gtk_widget_set_size_request(sb,
1513                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1514                                 AUX_SPINBUTTON_HEIGHT);
1515     gtk_widget_show(sb);
1516     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1517     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1518     gtk_container_add(GTK_CONTAINER(hb), sb);
1519     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1521     return hb;
1524 #define MODE_LABEL_WIDTH 70
1526 //########################
1527 //##       Star         ##
1528 //########################
1530 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1532     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1534     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1535         // do not remember prefs if this call is initiated by an undo change, because undoing object
1536         // creation sets bogus values to its attributes before it is deleted
1537         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1538     }
1540     // quit if run by the attr_changed listener
1541     if (g_object_get_data( dataKludge, "freeze" )) {
1542         return;
1543     }
1545     // in turn, prevent listener from responding
1546     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1548     bool modmade = false;
1550     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1551     GSList const *items = selection->itemList();
1552     for (; items != NULL; items = items->next) {
1553         if (SP_IS_STAR((SPItem *) items->data)) {
1554             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1555             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1556             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1557                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1558                                     + M_PI / (gint)adj->value));
1559             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1560             modmade = true;
1561         }
1562     }
1563     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1564                                    _("Star: Change number of corners"));
1566     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1569 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1571     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1573     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1574         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1575     }
1577     // quit if run by the attr_changed listener
1578     if (g_object_get_data( dataKludge, "freeze" )) {
1579         return;
1580     }
1582     // in turn, prevent listener from responding
1583     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1585     bool modmade = false;
1586     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1587     GSList const *items = selection->itemList();
1588     for (; items != NULL; items = items->next) {
1589         if (SP_IS_STAR((SPItem *) items->data)) {
1590             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1592             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1593             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1594             if (r2 < r1) {
1595                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1596             } else {
1597                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1598             }
1600             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1601             modmade = true;
1602         }
1603     }
1605     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1606                                    _("Star: Change spoke ratio"));
1608     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1611 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1613     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1614     bool flat = ege_select_one_action_get_active( act ) == 0;
1616     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1617         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1618                                     flat ? "true" : "false" );
1619     }
1621     // quit if run by the attr_changed listener
1622     if (g_object_get_data( dataKludge, "freeze" )) {
1623         return;
1624     }
1626     // in turn, prevent listener from responding
1627     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1629     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1630     GSList const *items = selection->itemList();
1631     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1632     bool modmade = false;
1634     if ( prop_action ) {
1635         gtk_action_set_sensitive( prop_action, !flat );
1636     }
1638     for (; items != NULL; items = items->next) {
1639         if (SP_IS_STAR((SPItem *) items->data)) {
1640             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1641             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1642             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1643             modmade = true;
1644         }
1645     }
1647     if (modmade) {
1648         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1649                          flat ? _("Make polygon") : _("Make star"));
1650     }
1652     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1655 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1657     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1659     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1660         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1661     }
1663     // quit if run by the attr_changed listener
1664     if (g_object_get_data( dataKludge, "freeze" )) {
1665         return;
1666     }
1668     // in turn, prevent listener from responding
1669     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1671     bool modmade = false;
1673     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1674     GSList const *items = selection->itemList();
1675     for (; items != NULL; items = items->next) {
1676         if (SP_IS_STAR((SPItem *) items->data)) {
1677             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1678             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1679             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1680             modmade = true;
1681         }
1682     }
1683     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1684                                    _("Star: Change rounding"));
1686     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1689 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1691     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1693     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1694         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1695     }
1697     // quit if run by the attr_changed listener
1698     if (g_object_get_data( dataKludge, "freeze" )) {
1699         return;
1700     }
1702     // in turn, prevent listener from responding
1703     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1705     bool modmade = false;
1707     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1708     GSList const *items = selection->itemList();
1709     for (; items != NULL; items = items->next) {
1710         if (SP_IS_STAR((SPItem *) items->data)) {
1711             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1712             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1713             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1714             modmade = true;
1715         }
1716     }
1717     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1718                                    _("Star: Change randomization"));
1720     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1724 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1725                                        gchar const */*old_value*/, gchar const */*new_value*/,
1726                                        bool /*is_interactive*/, gpointer data)
1728     GtkWidget *tbl = GTK_WIDGET(data);
1730     // quit if run by the _changed callbacks
1731     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1732         return;
1733     }
1735     // in turn, prevent callbacks from responding
1736     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1738     GtkAdjustment *adj = 0;
1740     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1741     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1743     if (!strcmp(name, "inkscape:randomized")) {
1744         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1745         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1746     } else if (!strcmp(name, "inkscape:rounded")) {
1747         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1748         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1749     } else if (!strcmp(name, "inkscape:flatsided")) {
1750         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1751         char const *flatsides = repr->attribute("inkscape:flatsided");
1752         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1753         if ( flatsides && !strcmp(flatsides,"false") ) {
1754             ege_select_one_action_set_active( flat_action, 1 );
1755             gtk_action_set_sensitive( prop_action, TRUE );
1756         } else {
1757             ege_select_one_action_set_active( flat_action, 0 );
1758             gtk_action_set_sensitive( prop_action, FALSE );
1759         }
1760     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1761         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1762         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1763         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1764         if (r2 < r1) {
1765             gtk_adjustment_set_value(adj, r2/r1);
1766         } else {
1767             gtk_adjustment_set_value(adj, r1/r2);
1768         }
1769     } else if (!strcmp(name, "sodipodi:sides")) {
1770         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1771         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1772     }
1774     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1778 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1780     NULL, /* child_added */
1781     NULL, /* child_removed */
1782     star_tb_event_attr_changed,
1783     NULL, /* content_changed */
1784     NULL  /* order_changed */
1785 };
1788 /**
1789  *  \param selection Should not be NULL.
1790  */
1791 static void
1792 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1794     int n_selected = 0;
1795     Inkscape::XML::Node *repr = NULL;
1797     purge_repr_listener( tbl, tbl );
1799     for (GSList const *items = selection->itemList();
1800          items != NULL;
1801          items = items->next)
1802     {
1803         if (SP_IS_STAR((SPItem *) items->data)) {
1804             n_selected++;
1805             repr = SP_OBJECT_REPR((SPItem *) items->data);
1806         }
1807     }
1809     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1811     if (n_selected == 0) {
1812         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1813     } else if (n_selected == 1) {
1814         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1816         if (repr) {
1817             g_object_set_data( tbl, "repr", repr );
1818             Inkscape::GC::anchor(repr);
1819             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1820             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1821         }
1822     } else {
1823         // FIXME: implement averaging of all parameters for multiple selected stars
1824         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1825         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1826     }
1830 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1832     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1833     // callbacks to lump all the changes for all selected objects in one undo step
1835     GtkAdjustment *adj = 0;
1837     // fixme: make settable in prefs!
1838     gint mag = 5;
1839     gdouble prop = 0.5;
1840     gboolean flat = FALSE;
1841     gdouble randomized = 0;
1842     gdouble rounded = 0;
1844     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1845     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1847     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1848     gtk_action_set_sensitive( sb2, !flat );
1850     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1851     gtk_adjustment_set_value(adj, mag);
1852     gtk_adjustment_value_changed(adj);
1854     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1855     gtk_adjustment_set_value(adj, prop);
1856     gtk_adjustment_value_changed(adj);
1858     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1859     gtk_adjustment_set_value(adj, rounded);
1860     gtk_adjustment_value_changed(adj);
1862     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1863     gtk_adjustment_set_value(adj, randomized);
1864     gtk_adjustment_value_changed(adj);
1868 void
1869 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1871     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1872     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1873     GtkWidget *l = gtk_label_new(NULL);
1874     gtk_label_set_markup(GTK_LABEL(l), title);
1875     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1876     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1877     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1881 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1883     {
1884         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1885         ege_output_action_set_use_markup( act, TRUE );
1886         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1887         g_object_set_data( holder, "mode_action", act );
1888     }
1890     {
1891         EgeAdjustmentAction* eact = 0;
1892         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1893         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1895         /* Flatsided checkbox */
1896         {
1897             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1899             GtkTreeIter iter;
1900             gtk_list_store_append( model, &iter );
1901             gtk_list_store_set( model, &iter,
1902                                 0, _("Polygon"),
1903                                 1, _("Regular polygon (with one handle) instead of a star"),
1904                                 2, "star_flat",
1905                                 -1 );
1907             gtk_list_store_append( model, &iter );
1908             gtk_list_store_set( model, &iter,
1909                                 0, _("Star"),
1910                                 1, _("Star instead of a regular polygon (with one handle)"),
1911                                 2, "star_angled",
1912                                 -1 );
1914             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1915             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1916             g_object_set_data( holder, "flat_action", act );
1918             ege_select_one_action_set_appearance( act, "full" );
1919             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1920             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1921             ege_select_one_action_set_icon_column( act, 2 );
1922             ege_select_one_action_set_tooltip_column( act, 1  );
1924             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1925             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1926         }
1928         /* Magnitude */
1929         {
1930         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1931         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1932         eact = create_adjustment_action( "MagnitudeAction",
1933                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1934                                          "tools.shapes.star", "magnitude", 3,
1935                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1936                                          3, 1024, 1, 5,
1937                                          labels, values, G_N_ELEMENTS(labels),
1938                                          sp_stb_magnitude_value_changed,
1939                                          1.0, 0 );
1940         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1941         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1942         }
1944         /* Spoke ratio */
1945         {
1946         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1947         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1948         eact = create_adjustment_action( "SpokeAction",
1949                                          _("Spoke ratio"), _("Spoke ratio:"),
1950                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1951                                          // Base radius is the same for the closest handle.
1952                                          _("Base radius to tip radius ratio"),
1953                                          "tools.shapes.star", "proportion", 0.5,
1954                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1955                                          0.01, 1.0, 0.01, 0.1,
1956                                          labels, values, G_N_ELEMENTS(labels),
1957                                          sp_stb_proportion_value_changed );
1958         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1959         g_object_set_data( holder, "prop_action", eact );
1960         }
1962         if ( !isFlatSided ) {
1963             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1964         } else {
1965             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1966         }
1968         /* Roundedness */
1969         {
1970         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1971         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1972         eact = create_adjustment_action( "RoundednessAction",
1973                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1974                                          "tools.shapes.star", "rounded", 0.0,
1975                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1976                                          -10.0, 10.0, 0.01, 0.1,
1977                                          labels, values, G_N_ELEMENTS(labels),
1978                                          sp_stb_rounded_value_changed );
1979         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1980         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1981         }
1983         /* Randomization */
1984         {
1985         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1986         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1987         eact = create_adjustment_action( "RandomizationAction",
1988                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1989                                          "tools.shapes.star", "randomized", 0.0,
1990                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1991                                          -10.0, 10.0, 0.001, 0.01,
1992                                          labels, values, G_N_ELEMENTS(labels),
1993                                          sp_stb_randomized_value_changed, 0.1, 3 );
1994         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1995         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1996         }
1997     }
1999     {
2000         /* Reset */
2001         {
2002             GtkAction* act = gtk_action_new( "StarResetAction",
2003                                              _("Defaults"),
2004                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2005                                              GTK_STOCK_CLEAR );
2006             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2007             gtk_action_group_add_action( mainActions, act );
2008             gtk_action_set_sensitive( act, TRUE );
2009         }
2010     }
2012     sigc::connection *connection = new sigc::connection(
2013         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2014         );
2015     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2016     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2020 //########################
2021 //##       Rect         ##
2022 //########################
2024 static void sp_rtb_sensitivize( GObject *tbl )
2026     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2027     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2028     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2030     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2031         gtk_action_set_sensitive( not_rounded, FALSE );
2032     } else {
2033         gtk_action_set_sensitive( not_rounded, TRUE );
2034     }
2038 static void
2039 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2040                           void (*setter)(SPRect *, gdouble))
2042     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2044     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2045     SPUnit const *unit = tracker->getActiveUnit();
2047     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2048         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2049     }
2051     // quit if run by the attr_changed listener
2052     if (g_object_get_data( tbl, "freeze" )) {
2053         return;
2054     }
2056     // in turn, prevent listener from responding
2057     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2059     bool modmade = false;
2060     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2061     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2062         if (SP_IS_RECT(items->data)) {
2063             if (adj->value != 0) {
2064                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2065             } else {
2066                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2067             }
2068             modmade = true;
2069         }
2070     }
2072     sp_rtb_sensitivize( tbl );
2074     if (modmade) {
2075         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2076                                    _("Change rectangle"));
2077     }
2079     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2082 static void
2083 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2085     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2088 static void
2089 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2091     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2094 static void
2095 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2097     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2100 static void
2101 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2103     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2108 static void
2109 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2111     GtkAdjustment *adj = 0;
2113     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2114     gtk_adjustment_set_value(adj, 0.0);
2115     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2116     gtk_adjustment_value_changed(adj);
2118     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2119     gtk_adjustment_set_value(adj, 0.0);
2120     gtk_adjustment_value_changed(adj);
2122     sp_rtb_sensitivize( obj );
2125 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2126                                        gchar const */*old_value*/, gchar const */*new_value*/,
2127                                        bool /*is_interactive*/, gpointer data)
2129     GObject *tbl = G_OBJECT(data);
2131     // quit if run by the _changed callbacks
2132     if (g_object_get_data( tbl, "freeze" )) {
2133         return;
2134     }
2136     // in turn, prevent callbacks from responding
2137     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2139     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2140     SPUnit const *unit = tracker->getActiveUnit();
2142     gpointer item = g_object_get_data( tbl, "item" );
2143     if (item && SP_IS_RECT(item)) {
2144         {
2145             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2146             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2147             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2148         }
2150         {
2151             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2152             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2153             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2154         }
2156         {
2157             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2158             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2159             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2160         }
2162         {
2163             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2164             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2165             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2166         }
2167     }
2169     sp_rtb_sensitivize( tbl );
2171     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2175 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2176     NULL, /* child_added */
2177     NULL, /* child_removed */
2178     rect_tb_event_attr_changed,
2179     NULL, /* content_changed */
2180     NULL  /* order_changed */
2181 };
2183 /**
2184  *  \param selection should not be NULL.
2185  */
2186 static void
2187 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2189     int n_selected = 0;
2190     Inkscape::XML::Node *repr = NULL;
2191     SPItem *item = NULL;
2193     if ( g_object_get_data( tbl, "repr" ) ) {
2194         g_object_set_data( tbl, "item", NULL );
2195     }
2196     purge_repr_listener( tbl, tbl );
2198     for (GSList const *items = selection->itemList();
2199          items != NULL;
2200          items = items->next) {
2201         if (SP_IS_RECT((SPItem *) items->data)) {
2202             n_selected++;
2203             item = (SPItem *) items->data;
2204             repr = SP_OBJECT_REPR(item);
2205         }
2206     }
2208     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2210     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2212     if (n_selected == 0) {
2213         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2215         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2216         gtk_action_set_sensitive(w, FALSE);
2217         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2218         gtk_action_set_sensitive(h, FALSE);
2220     } else if (n_selected == 1) {
2221         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2222         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2224         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2225         gtk_action_set_sensitive(w, TRUE);
2226         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2227         gtk_action_set_sensitive(h, TRUE);
2229         if (repr) {
2230             g_object_set_data( tbl, "repr", repr );
2231             g_object_set_data( tbl, "item", item );
2232             Inkscape::GC::anchor(repr);
2233             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2234             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2235         }
2236     } else {
2237         // FIXME: implement averaging of all parameters for multiple selected
2238         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2239         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2240         sp_rtb_sensitivize( tbl );
2241     }
2245 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2247     EgeAdjustmentAction* eact = 0;
2249     {
2250         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2251         ege_output_action_set_use_markup( act, TRUE );
2252         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2253         g_object_set_data( holder, "mode_action", act );
2254     }
2256     // rx/ry units menu: create
2257     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2258     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2259     // fixme: add % meaning per cent of the width/height
2260     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2261     g_object_set_data( holder, "tracker", tracker );
2263     /* W */
2264     {
2265         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2266         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2267         eact = create_adjustment_action( "RectWidthAction",
2268                                          _("Width"), _("W:"), _("Width of rectangle"),
2269                                          "tools.shapes.rect", "width", 0,
2270                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2271                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2272                                          labels, values, G_N_ELEMENTS(labels),
2273                                          sp_rtb_width_value_changed );
2274         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2275         g_object_set_data( holder, "width_action", eact );
2276         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2277         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2278     }
2280     /* H */
2281     {
2282         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2283         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2284         eact = create_adjustment_action( "RectHeightAction",
2285                                          _("Height"), _("H:"), _("Height of rectangle"),
2286                                          "tools.shapes.rect", "height", 0,
2287                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2288                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2289                                          labels, values, G_N_ELEMENTS(labels),
2290                                          sp_rtb_height_value_changed );
2291         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2292         g_object_set_data( holder, "height_action", eact );
2293         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2294         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2295     }
2297     /* rx */
2298     {
2299         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2300         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2301         eact = create_adjustment_action( "RadiusXAction",
2302                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2303                                          "tools.shapes.rect", "rx", 0,
2304                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2305                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2306                                          labels, values, G_N_ELEMENTS(labels),
2307                                          sp_rtb_rx_value_changed);
2308         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2309         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2310     }
2312     /* ry */
2313     {
2314         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2315         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2316         eact = create_adjustment_action( "RadiusYAction",
2317                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2318                                          "tools.shapes.rect", "ry", 0,
2319                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2320                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2321                                          labels, values, G_N_ELEMENTS(labels),
2322                                          sp_rtb_ry_value_changed);
2323         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2324         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2325     }
2327     // add the units menu
2328     {
2329         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2330         gtk_action_group_add_action( mainActions, act );
2331     }
2333     /* Reset */
2334     {
2335         InkAction* inky = ink_action_new( "RectResetAction",
2336                                           _("Not rounded"),
2337                                           _("Make corners sharp"),
2338                                           "squared_corner",
2339                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2340         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2341         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2342         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2343         g_object_set_data( holder, "not_rounded", inky );
2344     }
2346     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2347     sp_rtb_sensitivize( holder );
2349     sigc::connection *connection = new sigc::connection(
2350         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2351         );
2352     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2353     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2356 //########################
2357 //##       3D Box       ##
2358 //########################
2360 // normalize angle so that it lies in the interval [0,360]
2361 static double box3d_normalize_angle (double a) {
2362     double angle = a + ((int) (a/360.0))*360;
2363     if (angle < 0) {
2364         angle += 360.0;
2365     }
2366     return angle;
2369 static void
2370 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2371                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2372     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2373     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2374     //       are reset).
2375     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2377     if (is_infinite) {
2378         gtk_toggle_action_set_active(tact, TRUE);
2379         gtk_action_set_sensitive(act, TRUE);
2381         double angle = persp3d_get_infinite_angle(persp, axis);
2382         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2383             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2384         }
2385     } else {
2386         gtk_toggle_action_set_active(tact, FALSE);
2387         gtk_action_set_sensitive(act, FALSE);
2388     }
2391 static void
2392 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2393     if (!persp_repr) {
2394         g_print ("No perspective given to box3d_resync_toolbar().\n");
2395         return;
2396     }
2398     GtkWidget *tbl = GTK_WIDGET(data);
2399     GtkAdjustment *adj = 0;
2400     GtkAction *act = 0;
2401     GtkToggleAction *tact = 0;
2402     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2403     {
2404         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2405         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2406         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2408         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2409     }
2410     {
2411         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2412         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2413         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2415         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2416     }
2417     {
2418         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2419         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2420         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2422         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2423     }
2426 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2427                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2428                                                   bool /*is_interactive*/, gpointer data)
2430     GtkWidget *tbl = GTK_WIDGET(data);
2432     // quit if run by the attr_changed listener
2433     // note: it used to work without the differently called freeze_ attributes (here and in
2434     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2435     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2436         return;
2437     }
2439     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2440     // sp_document_maybe_done() when the document is undo insensitive)
2441     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2443     // TODO: Only update the appropriate part of the toolbar
2444 //    if (!strcmp(name, "inkscape:vp_z")) {
2445         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2446 //    }
2448     Persp3D *persp = persp3d_get_from_repr(repr);
2449     persp3d_update_box_reprs(persp);
2451     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2454 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2456     NULL, /* child_added */
2457     NULL, /* child_removed */
2458     box3d_persp_tb_event_attr_changed,
2459     NULL, /* content_changed */
2460     NULL  /* order_changed */
2461 };
2463 /**
2464  *  \param selection Should not be NULL.
2465  */
2466 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2467 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2468 static void
2469 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2471     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2472     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2473     // update the perspectives with infinite VPs and leave the other ones untouched).
2475     Inkscape::XML::Node *persp_repr = NULL;
2476     purge_repr_listener(tbl, tbl);
2478     SPItem *item = selection->singleItem();
2479     if (item && SP_IS_BOX3D(item)) {
2480         // FIXME: Also deal with multiple selected boxes
2481         SPBox3D *box = SP_BOX3D(item);
2482         Persp3D *persp = box3d_get_perspective(box);
2483         persp_repr = SP_OBJECT_REPR(persp);
2484         if (persp_repr) {
2485             g_object_set_data(tbl, "repr", persp_repr);
2486             Inkscape::GC::anchor(persp_repr);
2487             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2488             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2489         }
2491         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2492         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2494         box3d_resync_toolbar(persp_repr, tbl);
2495     }
2498 static void
2499 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2501     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2502     SPDocument *document = sp_desktop_document(desktop);
2504     // quit if run by the attr_changed listener
2505     // note: it used to work without the differently called freeze_ attributes (here and in
2506     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2507     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2508         return;
2509     }
2511     // in turn, prevent listener from responding
2512     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2514     //Persp3D *persp = document->current_persp3d;
2515     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2516     if (sel_persps.empty()) {
2517         // this can happen when the document is created; we silently ignore it
2518         return;
2519     }
2520     Persp3D *persp = *(sel_persps.begin());
2522     persp->tmat.set_infinite_direction (axis, adj->value);
2523     SP_OBJECT(persp)->updateRepr();
2525     // TODO: use the correct axis here, too
2526     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2528     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2532 static void
2533 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2535     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2538 static void
2539 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2541     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2544 static void
2545 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2547     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2551 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2553     // TODO: Take all selected perspectives into account
2554     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2555     if (sel_persps.empty()) {
2556         // this can happen when the document is created; we silently ignore it
2557         return;
2558     }
2559     Persp3D *persp = *(sel_persps.begin());
2561     bool set_infinite = gtk_toggle_action_get_active(act);
2562     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2565 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2567     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2570 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2572     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2575 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2577     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2580 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2582     EgeAdjustmentAction* eact = 0;
2583     SPDocument *document = sp_desktop_document (desktop);
2584     Persp3D *persp = document->current_persp3d;
2586     EgeAdjustmentAction* box3d_angle_x = 0;
2587     EgeAdjustmentAction* box3d_angle_y = 0;
2588     EgeAdjustmentAction* box3d_angle_z = 0;
2590     /* Angle X */
2591     {
2592         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2593         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2594         eact = create_adjustment_action( "3DBoxAngleXAction",
2595                                          _("Angle in X direction"), _("Angle X:"),
2596                                          // Translators: PL is short for 'perspective line'
2597                                          _("Angle of PLs in X direction"),
2598                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2599                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2600                                          -360.0, 360.0, 1.0, 10.0,
2601                                          labels, values, G_N_ELEMENTS(labels),
2602                                          box3d_angle_x_value_changed );
2603         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2604         g_object_set_data( holder, "box3d_angle_x_action", eact );
2605         box3d_angle_x = eact;
2606     }
2608     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2609         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2610     } else {
2611         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2612     }
2615     /* VP X state */
2616     {
2617         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2618                                                       // Translators: VP is short for 'vanishing point'
2619                                                       _("State of VP in X direction"),
2620                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2621                                                       "toggle_vp_x",
2622                                                       Inkscape::ICON_SIZE_DECORATION );
2623         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2624         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2625         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2626         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2627         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2628     }
2630     /* Angle Y */
2631     {
2632         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2633         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2634         eact = create_adjustment_action( "3DBoxAngleYAction",
2635                                          _("Angle in Y direction"), _("Angle Y:"),
2636                                          // Translators: PL is short for 'perspective line'
2637                                          _("Angle of PLs in Y direction"),
2638                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2639                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2640                                          -360.0, 360.0, 1.0, 10.0,
2641                                          labels, values, G_N_ELEMENTS(labels),
2642                                          box3d_angle_y_value_changed );
2643         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2644         g_object_set_data( holder, "box3d_angle_y_action", eact );
2645         box3d_angle_y = eact;
2646     }
2648     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2649         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2650     } else {
2651         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2652     }
2654     /* VP Y state */
2655     {
2656         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2657                                                       // Translators: VP is short for 'vanishing point'
2658                                                       _("State of VP in Y direction"),
2659                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2660                                                       "toggle_vp_y",
2661                                                       Inkscape::ICON_SIZE_DECORATION );
2662         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2663         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2664         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2665         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2666         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2667     }
2669     /* Angle Z */
2670     {
2671         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2672         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2673         eact = create_adjustment_action( "3DBoxAngleZAction",
2674                                          _("Angle in Z direction"), _("Angle Z:"),
2675                                          // Translators: PL is short for 'perspective line'
2676                                          _("Angle of PLs in Z direction"),
2677                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2678                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2679                                          -360.0, 360.0, 1.0, 10.0,
2680                                          labels, values, G_N_ELEMENTS(labels),
2681                                          box3d_angle_z_value_changed );
2682         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2683         g_object_set_data( holder, "box3d_angle_z_action", eact );
2684         box3d_angle_z = eact;
2685     }
2687     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2688         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2689     } else {
2690         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2691     }
2693     /* VP Z state */
2694     {
2695         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2696                                                       // Translators: VP is short for 'vanishing point'
2697                                                       _("State of VP in Z direction"),
2698                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2699                                                       "toggle_vp_z",
2700                                                       Inkscape::ICON_SIZE_DECORATION );
2701         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2702         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2703         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2704         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2705         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2706     }
2708     sigc::connection *connection = new sigc::connection(
2709         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2710        );
2711     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2712     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2715 //########################
2716 //##       Spiral       ##
2717 //########################
2719 static void
2720 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2722     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2724     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2725         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2726     }
2728     // quit if run by the attr_changed listener
2729     if (g_object_get_data( tbl, "freeze" )) {
2730         return;
2731     }
2733     // in turn, prevent listener from responding
2734     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2736     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2738     bool modmade = false;
2739     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2740          items != NULL;
2741          items = items->next)
2742     {
2743         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2744             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2745             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2746             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2747             modmade = true;
2748         }
2749     }
2751     g_free(namespaced_name);
2753     if (modmade) {
2754         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2755                                    _("Change spiral"));
2756     }
2758     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2761 static void
2762 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2764     sp_spl_tb_value_changed(adj, tbl, "revolution");
2767 static void
2768 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2770     sp_spl_tb_value_changed(adj, tbl, "expansion");
2773 static void
2774 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2776     sp_spl_tb_value_changed(adj, tbl, "t0");
2779 static void
2780 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2782     GtkWidget *tbl = GTK_WIDGET(obj);
2784     GtkAdjustment *adj;
2786     // fixme: make settable
2787     gdouble rev = 5;
2788     gdouble exp = 1.0;
2789     gdouble t0 = 0.0;
2791     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2792     gtk_adjustment_set_value(adj, rev);
2793     gtk_adjustment_value_changed(adj);
2795     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2796     gtk_adjustment_set_value(adj, exp);
2797     gtk_adjustment_value_changed(adj);
2799     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2800     gtk_adjustment_set_value(adj, t0);
2801     gtk_adjustment_value_changed(adj);
2803     spinbutton_defocus(GTK_OBJECT(tbl));
2807 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2808                                          gchar const */*old_value*/, gchar const */*new_value*/,
2809                                          bool /*is_interactive*/, gpointer data)
2811     GtkWidget *tbl = GTK_WIDGET(data);
2813     // quit if run by the _changed callbacks
2814     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2815         return;
2816     }
2818     // in turn, prevent callbacks from responding
2819     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2821     GtkAdjustment *adj;
2822     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2823     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2825     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2826     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2828     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2829     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2831     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2835 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2836     NULL, /* child_added */
2837     NULL, /* child_removed */
2838     spiral_tb_event_attr_changed,
2839     NULL, /* content_changed */
2840     NULL  /* order_changed */
2841 };
2843 static void
2844 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2846     int n_selected = 0;
2847     Inkscape::XML::Node *repr = NULL;
2849     purge_repr_listener( tbl, tbl );
2851     for (GSList const *items = selection->itemList();
2852          items != NULL;
2853          items = items->next)
2854     {
2855         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2856             n_selected++;
2857             repr = SP_OBJECT_REPR((SPItem *) items->data);
2858         }
2859     }
2861     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2863     if (n_selected == 0) {
2864         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2865     } else if (n_selected == 1) {
2866         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2868         if (repr) {
2869             g_object_set_data( tbl, "repr", repr );
2870             Inkscape::GC::anchor(repr);
2871             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2872             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2873         }
2874     } else {
2875         // FIXME: implement averaging of all parameters for multiple selected
2876         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2877         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2878     }
2882 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2884     EgeAdjustmentAction* eact = 0;
2886     {
2887         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2888         ege_output_action_set_use_markup( act, TRUE );
2889         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2890         g_object_set_data( holder, "mode_action", act );
2891     }
2893     /* Revolution */
2894     {
2895         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2896         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2897         eact = create_adjustment_action( "SpiralRevolutionAction",
2898                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2899                                          "tools.shapes.spiral", "revolution", 3.0,
2900                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2901                                          0.01, 1024.0, 0.1, 1.0,
2902                                          labels, values, G_N_ELEMENTS(labels),
2903                                          sp_spl_tb_revolution_value_changed, 1, 2);
2904         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2905     }
2907     /* Expansion */
2908     {
2909         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2910         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2911         eact = create_adjustment_action( "SpiralExpansionAction",
2912                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2913                                          "tools.shapes.spiral", "expansion", 1.0,
2914                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2915                                          0.0, 1000.0, 0.01, 1.0,
2916                                          labels, values, G_N_ELEMENTS(labels),
2917                                          sp_spl_tb_expansion_value_changed);
2918         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2919     }
2921     /* T0 */
2922     {
2923         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2924         gdouble values[] = {0, 0.5, 0.9};
2925         eact = create_adjustment_action( "SpiralT0Action",
2926                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2927                                          "tools.shapes.spiral", "t0", 0.0,
2928                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2929                                          0.0, 0.999, 0.01, 1.0,
2930                                          labels, values, G_N_ELEMENTS(labels),
2931                                          sp_spl_tb_t0_value_changed);
2932         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2933     }
2935     /* Reset */
2936     {
2937         InkAction* inky = ink_action_new( "SpiralResetAction",
2938                                           _("Defaults"),
2939                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2940                                           GTK_STOCK_CLEAR,
2941                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2942         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2943         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2944     }
2947     sigc::connection *connection = new sigc::connection(
2948         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2949         );
2950     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2951     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2954 //########################
2955 //##     Pen/Pencil    ##
2956 //########################
2959 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2961     // Put stuff here
2964 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2966     // Put stuff here
2969 //########################
2970 //##       Tweak        ##
2971 //########################
2973 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2975     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2978 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2980     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2983 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2985     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2988 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2990     int mode = ege_select_one_action_get_active( act );
2991     prefs_set_int_attribute("tools.tweak", "mode", mode);
2993     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2994     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2995     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2996     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2997     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2998     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2999     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3000         if (doh) gtk_action_set_sensitive (doh, TRUE);
3001         if (dos) gtk_action_set_sensitive (dos, TRUE);
3002         if (dol) gtk_action_set_sensitive (dol, TRUE);
3003         if (doo) gtk_action_set_sensitive (doo, TRUE);
3004         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3005         if (fid) gtk_action_set_sensitive (fid, FALSE);
3006     } else {
3007         if (doh) gtk_action_set_sensitive (doh, FALSE);
3008         if (dos) gtk_action_set_sensitive (dos, FALSE);
3009         if (dol) gtk_action_set_sensitive (dol, FALSE);
3010         if (doo) gtk_action_set_sensitive (doo, FALSE);
3011         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3012         if (fid) gtk_action_set_sensitive (fid, TRUE);
3013     }
3016 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3018     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3021 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3022     bool show = gtk_toggle_action_get_active( act );
3023     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3025 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3026     bool show = gtk_toggle_action_get_active( act );
3027     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3029 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3030     bool show = gtk_toggle_action_get_active( act );
3031     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3033 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3034     bool show = gtk_toggle_action_get_active( act );
3035     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3038 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3040     {
3041         /* Width */
3042         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3043         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3044         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3045                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3046                                                               "tools.tweak", "width", 15,
3047                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3048                                                               1, 100, 1.0, 10.0,
3049                                                               labels, values, G_N_ELEMENTS(labels),
3050                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3051         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3052         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3053     }
3056     {
3057         /* Force */
3058         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3059         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3060         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3061                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3062                                                               "tools.tweak", "force", 20,
3063                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3064                                                               1, 100, 1.0, 10.0,
3065                                                               labels, values, G_N_ELEMENTS(labels),
3066                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3067         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3068         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3069     }
3071     /* Mode */
3072     {
3073         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3075         GtkTreeIter iter;
3076         gtk_list_store_append( model, &iter );
3077         gtk_list_store_set( model, &iter,
3078                             0, _("Push mode"),
3079                             1, _("Push parts of paths in any direction"),
3080                             2, "tweak_push_mode",
3081                             -1 );
3083         gtk_list_store_append( model, &iter );
3084         gtk_list_store_set( model, &iter,
3085                             0, _("Shrink mode"),
3086                             1, _("Shrink (inset) parts of paths"),
3087                             2, "tweak_shrink_mode",
3088                             -1 );
3090         gtk_list_store_append( model, &iter );
3091         gtk_list_store_set( model, &iter,
3092                             0, _("Grow mode"),
3093                             1, _("Grow (outset) parts of paths"),
3094                             2, "tweak_grow_mode",
3095                             -1 );
3097         gtk_list_store_append( model, &iter );
3098         gtk_list_store_set( model, &iter,
3099                             0, _("Attract mode"),
3100                             1, _("Attract parts of paths towards cursor"),
3101                             2, "tweak_attract_mode",
3102                             -1 );
3104         gtk_list_store_append( model, &iter );
3105         gtk_list_store_set( model, &iter,
3106                             0, _("Repel mode"),
3107                             1, _("Repel parts of paths from cursor"),
3108                             2, "tweak_repel_mode",
3109                             -1 );
3111         gtk_list_store_append( model, &iter );
3112         gtk_list_store_set( model, &iter,
3113                             0, _("Roughen mode"),
3114                             1, _("Roughen parts of paths"),
3115                             2, "tweak_roughen_mode",
3116                             -1 );
3118         gtk_list_store_append( model, &iter );
3119         gtk_list_store_set( model, &iter,
3120                             0, _("Color paint mode"),
3121                             1, _("Paint the tool's color upon selected objects"),
3122                             2, "tweak_colorpaint_mode",
3123                             -1 );
3125         gtk_list_store_append( model, &iter );
3126         gtk_list_store_set( model, &iter,
3127                             0, _("Color jitter mode"),
3128                             1, _("Jitter the colors of selected objects"),
3129                             2, "tweak_colorjitter_mode",
3130                             -1 );
3132         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3133         g_object_set( act, "short_label", _("Mode:"), NULL );
3134         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3135         g_object_set_data( holder, "mode_action", act );
3137         ege_select_one_action_set_appearance( act, "full" );
3138         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3139         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3140         ege_select_one_action_set_icon_column( act, 2 );
3141         ege_select_one_action_set_tooltip_column( act, 1  );
3143         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3144         ege_select_one_action_set_active( act, mode );
3145         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3147         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3148     }
3150     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3152     {
3153         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3154         ege_output_action_set_use_markup( act, TRUE );
3155         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3156         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3157             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3158         g_object_set_data( holder, "tweak_channels_label", act);
3159     }
3161     {
3162         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3163                                                       _("Hue"),
3164                                                       _("In color mode, act on objects' hue"),
3165                                                       NULL,
3166                                                       Inkscape::ICON_SIZE_DECORATION );
3167         g_object_set( act, "short_label", _("H"), NULL );
3168         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3169         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3170         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3171         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3172             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3173         g_object_set_data( holder, "tweak_doh", act);
3174     }
3175     {
3176         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3177                                                       _("Saturation"),
3178                                                       _("In color mode, act on objects' saturation"),
3179                                                       NULL,
3180                                                       Inkscape::ICON_SIZE_DECORATION );
3181         g_object_set( act, "short_label", _("S"), NULL );
3182         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3183         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3184         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3185         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3186             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3187         g_object_set_data( holder, "tweak_dos", act );
3188     }
3189     {
3190         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3191                                                       _("Lightness"),
3192                                                       _("In color mode, act on objects' lightness"),
3193                                                       NULL,
3194                                                       Inkscape::ICON_SIZE_DECORATION );
3195         g_object_set( act, "short_label", _("L"), NULL );
3196         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3197         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3198         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3199         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3200             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3201         g_object_set_data( holder, "tweak_dol", act );
3202     }
3203     {
3204         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3205                                                       _("Opacity"),
3206                                                       _("In color mode, act on objects' opacity"),
3207                                                       NULL,
3208                                                       Inkscape::ICON_SIZE_DECORATION );
3209         g_object_set( act, "short_label", _("O"), NULL );
3210         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3211         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3212         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3213         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3214             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3215         g_object_set_data( holder, "tweak_doo", act );
3216     }
3218     {   /* Fidelity */
3219         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3220         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3221         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3222                                                               _("Fidelity"), _("Fidelity:"),
3223                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3224                                                               "tools.tweak", "fidelity", 50,
3225                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3226                                                               1, 100, 1.0, 10.0,
3227                                                               labels, values, G_N_ELEMENTS(labels),
3228                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3229         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3230         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3231         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3232             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3233         g_object_set_data( holder, "tweak_fidelity", eact );
3234     }
3237     /* Use Pressure button */
3238     {
3239         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3240                                                       _("Pressure"),
3241                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3242                                                       "use_pressure",
3243                                                       Inkscape::ICON_SIZE_DECORATION );
3244         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3245         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3246         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3247     }
3252 //########################
3253 //##     Calligraphy    ##
3254 //########################
3256 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3258     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3261 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3263     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3266 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3268     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3271 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3273     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3276 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3278     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3281 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3283     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3286 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3288     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3291 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3293     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3296 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3298     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3301 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3303     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3306 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3308     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3310     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3313 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3315     // FIXME: make defaults settable via Inkscape Options
3316     struct KeyValue {
3317         char const *key;
3318         double value;
3319     } const key_values[] = {
3320         {"mass", 0.02},
3321         {"wiggle", 0.0},
3322         {"angle", 30.0},
3323         {"width", 15},
3324         {"thinning", 0.1},
3325         {"tremor", 0.0},
3326         {"flatness", 0.9},
3327         {"cap_rounding", 0.0}
3328     };
3330     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3331         KeyValue const &kv = key_values[i];
3332         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3333         if ( adj ) {
3334             gtk_adjustment_set_value(adj, kv.value);
3335         }
3336     }
3340 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3342     {
3343         EgeAdjustmentAction* calligraphy_angle = 0;
3345         {
3346         /* Width */
3347         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3348         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3349         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3350                                                               _("Pen Width"), _("Width:"),
3351                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3352                                                               "tools.calligraphic", "width", 15,
3353                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3354                                                               1, 100, 1.0, 10.0,
3355                                                               labels, values, G_N_ELEMENTS(labels),
3356                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3357         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3358         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3359         }
3361         {
3362         /* Thinning */
3363             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3364             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3365         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3366                                                               _("Stroke Thinning"), _("Thinning:"),
3367                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3368                                                               "tools.calligraphic", "thinning", 0.1,
3369                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3370                                                               -1.0, 1.0, 0.01, 0.1,
3371                                                               labels, values, G_N_ELEMENTS(labels),
3372                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3373         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3374         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3375         }
3377         {
3378         /* Angle */
3379         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3380         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3381         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3382                                                               _("Pen Angle"), _("Angle:"),
3383                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3384                                                               "tools.calligraphic", "angle", 30,
3385                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3386                                                               -90.0, 90.0, 1.0, 10.0,
3387                                                               labels, values, G_N_ELEMENTS(labels),
3388                                                               sp_ddc_angle_value_changed, 1, 0 );
3389         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3390         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3391         calligraphy_angle = eact;
3392         }
3394         {
3395         /* Fixation */
3396             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3397         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3398         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3399                                                               _("Fixation"), _("Fixation:"),
3400                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3401                                                               "tools.calligraphic", "flatness", 0.9,
3402                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3403                                                               0.0, 1.0, 0.01, 0.1,
3404                                                               labels, values, G_N_ELEMENTS(labels),
3405                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3406         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3407         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3408         }
3410         {
3411         /* Cap Rounding */
3412             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3413         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3414         // TRANSLATORS: "cap" means "end" (both start and finish) here
3415         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3416                                                               _("Cap rounding"), _("Caps:"),
3417                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3418                                                               "tools.calligraphic", "cap_rounding", 0.0,
3419                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3420                                                               0.0, 5.0, 0.01, 0.1,
3421                                                               labels, values, G_N_ELEMENTS(labels),
3422                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3423         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3424         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3425         }
3427         {
3428         /* Tremor */
3429             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3430         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3431         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3432                                                               _("Stroke Tremor"), _("Tremor:"),
3433                                                               _("Increase to make strokes rugged and trembling"),
3434                                                               "tools.calligraphic", "tremor", 0.0,
3435                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3436                                                               0.0, 1.0, 0.01, 0.1,
3437                                                               labels, values, G_N_ELEMENTS(labels),
3438                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3440         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3441         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3442         }
3444         {
3445         /* Wiggle */
3446         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3447         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3448         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3449                                                               _("Pen Wiggle"), _("Wiggle:"),
3450                                                               _("Increase to make the pen waver and wiggle"),
3451                                                               "tools.calligraphic", "wiggle", 0.0,
3452                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3453                                                               0.0, 1.0, 0.01, 0.1,
3454                                                               labels, values, G_N_ELEMENTS(labels),
3455                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3456         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3457         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3458         }
3460         {
3461         /* Mass */
3462             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3463         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3464         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3465                                                               _("Pen Mass"), _("Mass:"),
3466                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3467                                                               "tools.calligraphic", "mass", 0.02,
3468                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3469                                                               0.0, 1.0, 0.01, 0.1,
3470                                                               labels, values, G_N_ELEMENTS(labels),
3471                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3472         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3473         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3474         }
3477         /* Trace Background button */
3478         {
3479             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3480                                                           _("Trace Background"),
3481                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3482                                                           "trace_background",
3483                                                           Inkscape::ICON_SIZE_DECORATION );
3484             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3485             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3486             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3487         }
3489         /* Use Pressure button */
3490         {
3491             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3492                                                           _("Pressure"),
3493                                                           _("Use the pressure of the input device to alter the width of the pen"),
3494                                                           "use_pressure",
3495                                                           Inkscape::ICON_SIZE_DECORATION );
3496             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3497             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3498             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3499         }
3501         /* Use Tilt button */
3502         {
3503             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3504                                                           _("Tilt"),
3505                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3506                                                           "use_tilt",
3507                                                           Inkscape::ICON_SIZE_DECORATION );
3508             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3509             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3510             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3511             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3512         }
3514         /* Reset */
3515         {
3516             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3517                                              _("Defaults"),
3518                                              _("Reset all parameters to defaults"),
3519                                              GTK_STOCK_CLEAR );
3520             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3521             gtk_action_group_add_action( mainActions, act );
3522             gtk_action_set_sensitive( act, TRUE );
3523         }
3524     }
3528 //########################
3529 //##    Circle / Arc    ##
3530 //########################
3532 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3534     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3535     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3537     if (v1 == 0 && v2 == 0) {
3538         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3539             gtk_action_set_sensitive( ocb, FALSE );
3540             gtk_action_set_sensitive( make_whole, FALSE );
3541         }
3542     } else {
3543         gtk_action_set_sensitive( ocb, TRUE );
3544         gtk_action_set_sensitive( make_whole, TRUE );
3545     }
3548 static void
3549 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3551     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3553     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3554         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3555     }
3557     // quit if run by the attr_changed listener
3558     if (g_object_get_data( tbl, "freeze" )) {
3559         return;
3560     }
3562     // in turn, prevent listener from responding
3563     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3565     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3567     bool modmade = false;
3568     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3569          items != NULL;
3570          items = items->next)
3571     {
3572         SPItem *item = SP_ITEM(items->data);
3574         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3576             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3577             SPArc *arc = SP_ARC(item);
3579             if (!strcmp(value_name, "start"))
3580                 ge->start = (adj->value * M_PI)/ 180;
3581             else
3582                 ge->end = (adj->value * M_PI)/ 180;
3584             sp_genericellipse_normalize(ge);
3585             ((SPObject *)arc)->updateRepr();
3586             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3588             modmade = true;
3589         }
3590     }
3592     g_free(namespaced_name);
3594     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3596     sp_arctb_sensitivize( tbl, adj->value, other->value );
3598     if (modmade) {
3599         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3600                                    _("Arc: Change start/end"));
3601     }
3603     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3607 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3609     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3612 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3614     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3617 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3619     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3620     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3621         if ( ege_select_one_action_get_active( act ) != 0 ) {
3622             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3623         } else {
3624             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3625         }
3626     }
3628     // quit if run by the attr_changed listener
3629     if (g_object_get_data( tbl, "freeze" )) {
3630         return;
3631     }
3633     // in turn, prevent listener from responding
3634     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3636     bool modmade = false;
3638     if ( ege_select_one_action_get_active(act) != 0 ) {
3639         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3640              items != NULL;
3641              items = items->next)
3642         {
3643             if (SP_IS_ARC((SPItem *) items->data)) {
3644                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3645                 repr->setAttribute("sodipodi:open", "true");
3646                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3647                 modmade = true;
3648             }
3649         }
3650     } else {
3651         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3652              items != NULL;
3653              items = items->next)
3654         {
3655             if (SP_IS_ARC((SPItem *) items->data))    {
3656                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3657                 repr->setAttribute("sodipodi:open", NULL);
3658                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3659                 modmade = true;
3660             }
3661         }
3662     }
3664     if (modmade) {
3665         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3666                                    _("Arc: Change open/closed"));
3667     }
3669     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3672 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3674     GtkAdjustment *adj;
3675     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3676     gtk_adjustment_set_value(adj, 0.0);
3677     gtk_adjustment_value_changed(adj);
3679     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3680     gtk_adjustment_set_value(adj, 0.0);
3681     gtk_adjustment_value_changed(adj);
3683     spinbutton_defocus( GTK_OBJECT(obj) );
3686 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3687                                       gchar const */*old_value*/, gchar const */*new_value*/,
3688                                       bool /*is_interactive*/, gpointer data)
3690     GObject *tbl = G_OBJECT(data);
3692     // quit if run by the _changed callbacks
3693     if (g_object_get_data( tbl, "freeze" )) {
3694         return;
3695     }
3697     // in turn, prevent callbacks from responding
3698     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3700     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3701     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3703     GtkAdjustment *adj1,*adj2;
3704     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3705     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3706     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3707     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3709     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3711     char const *openstr = NULL;
3712     openstr = repr->attribute("sodipodi:open");
3713     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3715     if (openstr) {
3716         ege_select_one_action_set_active( ocb, 1 );
3717     } else {
3718         ege_select_one_action_set_active( ocb, 0 );
3719     }
3721     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3724 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3725     NULL, /* child_added */
3726     NULL, /* child_removed */
3727     arc_tb_event_attr_changed,
3728     NULL, /* content_changed */
3729     NULL  /* order_changed */
3730 };
3733 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3735     int n_selected = 0;
3736     Inkscape::XML::Node *repr = NULL;
3738     purge_repr_listener( tbl, tbl );
3740     for (GSList const *items = selection->itemList();
3741          items != NULL;
3742          items = items->next)
3743     {
3744         if (SP_IS_ARC((SPItem *) items->data)) {
3745             n_selected++;
3746             repr = SP_OBJECT_REPR((SPItem *) items->data);
3747         }
3748     }
3750     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3752     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3753     if (n_selected == 0) {
3754         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3755     } else if (n_selected == 1) {
3756         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3757         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3759         if (repr) {
3760             g_object_set_data( tbl, "repr", repr );
3761             Inkscape::GC::anchor(repr);
3762             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3763             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3764         }
3765     } else {
3766         // FIXME: implement averaging of all parameters for multiple selected
3767         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3768         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3769         sp_arctb_sensitivize( tbl, 1, 0 );
3770     }
3774 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3776     EgeAdjustmentAction* eact = 0;
3779     {
3780         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3781         ege_output_action_set_use_markup( act, TRUE );
3782         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3783         g_object_set_data( holder, "mode_action", act );
3784     }
3786     /* Start */
3787     {
3788         eact = create_adjustment_action( "ArcStartAction",
3789                                          _("Start"), _("Start:"),
3790                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3791                                          "tools.shapes.arc", "start", 0.0,
3792                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3793                                          -360.0, 360.0, 1.0, 10.0,
3794                                          0, 0, 0,
3795                                          sp_arctb_start_value_changed);
3796         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3797     }
3799     /* End */
3800     {
3801         eact = create_adjustment_action( "ArcEndAction",
3802                                          _("End"), _("End:"),
3803                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3804                                          "tools.shapes.arc", "end", 0.0,
3805                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3806                                          -360.0, 360.0, 1.0, 10.0,
3807                                          0, 0, 0,
3808                                          sp_arctb_end_value_changed);
3809         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3810     }
3812     /* Segments / Pie checkbox */
3813     {
3814         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3816         GtkTreeIter iter;
3817         gtk_list_store_append( model, &iter );
3818         gtk_list_store_set( model, &iter,
3819                             0, _("Closed arc"),
3820                             1, _("Switch to segment (closed shape with two radii)"),
3821                             2, "circle_closed_arc",
3822                             -1 );
3824         gtk_list_store_append( model, &iter );
3825         gtk_list_store_set( model, &iter,
3826                             0, _("Open Arc"),
3827                             1, _("Switch to arc (unclosed shape)"),
3828                             2, "circle_open_arc",
3829                             -1 );
3831         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3832         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3833         g_object_set_data( holder, "open_action", act );
3835         ege_select_one_action_set_appearance( act, "full" );
3836         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3837         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3838         ege_select_one_action_set_icon_column( act, 2 );
3839         ege_select_one_action_set_tooltip_column( act, 1  );
3841         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3842         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3843         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3844         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3845     }
3847     /* Make Whole */
3848     {
3849         InkAction* inky = ink_action_new( "ArcResetAction",
3850                                           _("Make whole"),
3851                                           _("Make the shape a whole ellipse, not arc or segment"),
3852                                           "reset_circle",
3853                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3854         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3855         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3856         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3857         g_object_set_data( holder, "make_whole", inky );
3858     }
3860     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3861     // sensitivize make whole and open checkbox
3862     {
3863         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3864         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3865         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3866     }
3869     sigc::connection *connection = new sigc::connection(
3870         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3871         );
3872     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3873     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3879 // toggle button callbacks and updaters
3881 //########################
3882 //##      Dropper       ##
3883 //########################
3885 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3886     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3887     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3888     if ( set_action ) {
3889         if ( gtk_toggle_action_get_active( act ) ) {
3890             gtk_action_set_sensitive( set_action, TRUE );
3891         } else {
3892             gtk_action_set_sensitive( set_action, FALSE );
3893         }
3894     }
3896     spinbutton_defocus(GTK_OBJECT(tbl));
3899 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3900     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3901     spinbutton_defocus(GTK_OBJECT(tbl));
3905 /**
3906  * Dropper auxiliary toolbar construction and setup.
3907  *
3908  * TODO: Would like to add swatch of current color.
3909  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3910  *       can drag and drop places. Will provide a nice mixing palette.
3911  */
3912 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3914     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3916     {
3917         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3918                                                       _("Pick alpha"),
3919                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3920                                                       "color_alpha_get",
3921                                                       Inkscape::ICON_SIZE_DECORATION );
3922         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3923         g_object_set_data( holder, "pick_action", act );
3924         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3925         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3926     }
3928     {
3929         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3930                                                       _("Set alpha"),
3931                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3932                                                       "color_alpha_set",
3933                                                       Inkscape::ICON_SIZE_DECORATION );
3934         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3935         g_object_set_data( holder, "set_action", act );
3936         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3937         // make sure it's disabled if we're not picking alpha
3938         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3939         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3940     }
3944 //########################
3945 //##    Text Toolbox    ##
3946 //########################
3947 /*
3948 static void
3949 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3951     //Call back for letter sizing spinbutton
3954 static void
3955 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3957     //Call back for line height spinbutton
3960 static void
3961 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3963     //Call back for horizontal kerning spinbutton
3966 static void
3967 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3969     //Call back for vertical kerning spinbutton
3972 static void
3973 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3975     //Call back for letter rotation spinbutton
3976 }*/
3978 namespace {
3980 bool popdown_visible = false;
3981 bool popdown_hasfocus = false;
3983 void
3984 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3986     SPStyle *query =
3987         sp_style_new (SP_ACTIVE_DOCUMENT);
3989     int result_fontspec =
3990         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3992     int result_family =
3993         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3995     int result_style =
3996         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3998     int result_numbers =
3999         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4001     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4003     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4004     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4005     {
4006         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4008         if (repr)
4009         {
4010             sp_style_read_from_repr (query, repr);
4011         }
4012         else
4013         {
4014             return;
4015         }
4016     }
4018     if (query->text)
4019     {
4020         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4021             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4022             gtk_entry_set_text (GTK_ENTRY (entry), "");
4024         } else if (query->text->font_specification.value || query->text->font_family.value) {
4026             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4028             // Get the font that corresponds
4029             Glib::ustring familyName;
4031             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4032             if (font) {
4033                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4034                 font->Unref();
4035                 font = NULL;
4036             }
4038             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4040             Gtk::TreePath path;
4041             try {
4042                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4043             } catch (...) {
4044                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4045                 return;
4046             }
4048             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4049             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4051             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4053             gtk_tree_selection_select_path (tselection, path.gobj());
4054             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4056             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4057         }
4059         //Size
4060         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4061         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4062         g_object_set_data (tbl, "size-block", gpointer(1));
4063         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4064         g_object_set_data (tbl, "size-block", gpointer(0));
4065         free (str);
4067         //Anchor
4068         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4069         {
4070             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4071             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4072             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4073             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4074         }
4075         else
4076         {
4077             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4078             {
4079                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4080                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4081                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4082                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4083             }
4084             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4085             {
4086                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4087                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4088                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4089                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4090             }
4091             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4092             {
4093                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4094                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4095                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4096                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4097             }
4098         }
4100         //Style
4101         {
4102             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4104             gboolean active = gtk_toggle_button_get_active (button);
4105             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4107             if (active != check)
4108             {
4109                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4110                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4111                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4112             }
4113         }
4115         {
4116             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4118             gboolean active = gtk_toggle_button_get_active (button);
4119             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4121             if (active != check)
4122             {
4123                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4124                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4125                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4126             }
4127         }
4129         //Orientation
4130         //locking both buttons, changing one affect all group (both)
4131         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4132         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4134         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4135         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4137         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4138         {
4139             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4140         }
4141         else
4142         {
4143             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4144         }
4145         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4146         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4147     }
4149     sp_style_unref(query);
4152 void
4153 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4155     sp_text_toolbox_selection_changed (selection, tbl);
4158 void
4159 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4161     sp_text_toolbox_selection_changed (NULL, tbl);
4164 void
4165 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4166                                 GObject             *tbl)
4168     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4169     GtkTreeModel *model = 0;
4170     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4171     GtkTreeIter   iter;
4172     char         *family = 0;
4174     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4175     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4177     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4178         return;
4179     }
4181     gtk_tree_model_get (model, &iter, 0, &family, -1);
4183     if (g_object_get_data (G_OBJECT (selection), "block"))
4184     {
4185         gtk_entry_set_text (GTK_ENTRY (entry), family);
4186         return;
4187     }
4189     gtk_entry_set_text (GTK_ENTRY (entry), family);
4191     SPStyle *query =
4192         sp_style_new (SP_ACTIVE_DOCUMENT);
4194     int result_fontspec =
4195         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4197     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4199     SPCSSAttr *css = sp_repr_css_attr_new ();
4202     // First try to get the font spec from the stored value
4203     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4205     if (fontSpec.empty()) {
4206         // Construct a new font specification if it does not yet exist
4207         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4208         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4209         fontFromStyle->Unref();
4210     }
4212     if (!fontSpec.empty()) {
4213         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4214         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4215             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4216             if (font) {
4217                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4219                 // Set all the these just in case they were altered when finding the best
4220                 // match for the new family and old style...
4222                 gchar c[256];
4224                 font->Family(c, 256);
4225                 sp_repr_css_set_property (css, "font-family", c);
4227                 font->Attribute( "weight", c, 256);
4228                 sp_repr_css_set_property (css, "font-weight", c);
4230                 font->Attribute("style", c, 256);
4231                 sp_repr_css_set_property (css, "font-style", c);
4233                 font->Attribute("stretch", c, 256);
4234                 sp_repr_css_set_property (css, "font-stretch", c);
4236                 font->Attribute("variant", c, 256);
4237                 sp_repr_css_set_property (css, "font-variant", c);
4239                 font->Unref();
4240             }
4241         }
4242     }
4244     // If querying returned nothing, set the default style of the tool (for new texts)
4245     if (result_fontspec == QUERY_STYLE_NOTHING)
4246     {
4247         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4248         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4249     }
4250     else
4251     {
4252         sp_desktop_set_style (desktop, css, true, true);
4253     }
4255     sp_style_unref(query);
4257     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4258                                    _("Text: Change font family"));
4259     sp_repr_css_attr_unref (css);
4260     free (family);
4261     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4263     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4266 void
4267 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4268                                        GObject      *tbl)
4270     const char *family = gtk_entry_get_text (entry);
4272     try {
4273         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4274         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4275         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4276         gtk_tree_selection_select_path (selection, path.gobj());
4277         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4278         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4279     } catch (...) {
4280         if (family && strlen (family))
4281         {
4282             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4283         }
4284     }
4287 void
4288 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4289                                    gpointer          data)
4291     if (g_object_get_data (G_OBJECT (button), "block")) return;
4292     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4293     int prop = GPOINTER_TO_INT(data);
4295     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4296     SPCSSAttr *css = sp_repr_css_attr_new ();
4298     switch (prop)
4299     {
4300         case 0:
4301         {
4302             sp_repr_css_set_property (css, "text-anchor", "start");
4303             sp_repr_css_set_property (css, "text-align", "start");
4304             break;
4305         }
4306         case 1:
4307         {
4308             sp_repr_css_set_property (css, "text-anchor", "middle");
4309             sp_repr_css_set_property (css, "text-align", "center");
4310             break;
4311         }
4313         case 2:
4314         {
4315             sp_repr_css_set_property (css, "text-anchor", "end");
4316             sp_repr_css_set_property (css, "text-align", "end");
4317             break;
4318         }
4320         case 3:
4321         {
4322             sp_repr_css_set_property (css, "text-anchor", "start");
4323             sp_repr_css_set_property (css, "text-align", "justify");
4324             break;
4325         }
4326     }
4328     SPStyle *query =
4329         sp_style_new (SP_ACTIVE_DOCUMENT);
4330     int result_numbers =
4331         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4333     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4334     if (result_numbers == QUERY_STYLE_NOTHING)
4335     {
4336         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4337     }
4339     sp_style_unref(query);
4341     sp_desktop_set_style (desktop, css, true, true);
4342     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4343                                    _("Text: Change alignment"));
4344     sp_repr_css_attr_unref (css);
4346     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4349 void
4350 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4351                                gpointer          data)
4353     if (g_object_get_data (G_OBJECT (button), "block")) return;
4355     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4356     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4357     int          prop       = GPOINTER_TO_INT(data);
4358     bool         active     = gtk_toggle_button_get_active (button);
4360     SPStyle *query =
4361         sp_style_new (SP_ACTIVE_DOCUMENT);
4363     int result_fontspec =
4364         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4366     int result_family =
4367         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4369     int result_style =
4370         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4372     int result_numbers =
4373         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4375     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4376     Glib::ustring newFontSpec = "";
4378     if (fontSpec.empty()) {
4379         // Construct a new font specification if it does not yet exist
4380         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4381         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4382         fontFromStyle->Unref();
4383     }
4385     switch (prop)
4386     {
4387         case 0:
4388         {
4389             if (!fontSpec.empty()) {
4390                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4391             }
4392             if (fontSpec != newFontSpec) {
4393                 // Don't even set the bold if the font didn't exist on the system
4394                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4395             }
4396             break;
4397         }
4399         case 1:
4400         {
4401             if (!fontSpec.empty()) {
4402                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4403             }
4404             if (fontSpec != newFontSpec) {
4405                 // Don't even set the italic if the font didn't exist on the system
4406                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4407             }
4408             break;
4409         }
4410     }
4412     if (!newFontSpec.empty()) {
4413         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4414     }
4416     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4417     if (result_fontspec == QUERY_STYLE_NOTHING)
4418     {
4419         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4420     }
4422     sp_style_unref(query);
4424     sp_desktop_set_style (desktop, css, true, true);
4425     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4426                                    _("Text: Change font style"));
4427     sp_repr_css_attr_unref (css);
4429     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4432 void
4433 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4434                                      gpointer         data)
4436     if (g_object_get_data (G_OBJECT (button), "block")) {
4437         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4438         return;
4439     }
4441     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4442     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4443     int          prop       = GPOINTER_TO_INT(data);
4445     switch (prop)
4446     {
4447         case 0:
4448         {
4449             sp_repr_css_set_property (css, "writing-mode", "lr");
4450             break;
4451         }
4453         case 1:
4454         {
4455             sp_repr_css_set_property (css, "writing-mode", "tb");
4456             break;
4457         }
4458     }
4460     SPStyle *query =
4461         sp_style_new (SP_ACTIVE_DOCUMENT);
4462     int result_numbers =
4463         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4465     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4466     if (result_numbers == QUERY_STYLE_NOTHING)
4467     {
4468         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4469     }
4471     sp_desktop_set_style (desktop, css, true, true);
4472     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4473                                    _("Text: Change orientation"));
4474     sp_repr_css_attr_unref (css);
4476     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4479 gboolean
4480 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4482     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4483     if (!desktop) return FALSE;
4485     switch (get_group0_keyval (event)) {
4486         case GDK_Escape: // defocus
4487             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4488             sp_text_toolbox_selection_changed (NULL, tbl); // update
4489             return TRUE; // I consumed the event
4490             break;
4491     }
4492     return FALSE;
4495 gboolean
4496 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4498     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4499     if (!desktop) return FALSE;
4501     switch (get_group0_keyval (event)) {
4502         case GDK_KP_Enter:
4503         case GDK_Return:
4504         case GDK_Escape: // defocus
4505             gtk_widget_hide (w);
4506             popdown_visible = false;
4507             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4508             return TRUE; // I consumed the event
4509             break;
4510         case GDK_w:
4511         case GDK_W:
4512             if (event->state & GDK_CONTROL_MASK) {
4513                 gtk_widget_hide (w);
4514                 popdown_visible = false;
4515                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4516                 return TRUE; // I consumed the event
4517             }
4518             break;
4519     }
4520     return FALSE;
4524 void
4525 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4526                                GObject     *tbl)
4528     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4530     if (g_object_get_data (tbl, "size-block")) return;
4532     // If this is not from selecting a size in the list (in which case get_active will give the
4533     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4534     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4535     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4536     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4537         return;
4539     gchar *endptr;
4540     gdouble value = -1;
4541     char *text = gtk_combo_box_get_active_text (cbox);
4542     if (text) {
4543         value = g_strtod (text, &endptr);
4544         if (endptr == text) // conversion failed, non-numeric input
4545             value = -1;
4546         free (text);
4547     }
4548     if (value <= 0) {
4549         return; // could not parse value
4550     }
4552     SPCSSAttr *css = sp_repr_css_attr_new ();
4553     Inkscape::CSSOStringStream osfs;
4554     osfs << value;
4555     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4557     SPStyle *query =
4558         sp_style_new (SP_ACTIVE_DOCUMENT);
4559     int result_numbers =
4560         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4562     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4563     if (result_numbers == QUERY_STYLE_NOTHING)
4564     {
4565         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4566     }
4568     sp_style_unref(query);
4570     sp_desktop_set_style (desktop, css, true, true);
4571     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4572                                    _("Text: Change font size"));
4573     sp_repr_css_attr_unref (css);
4575     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4578 gboolean
4579 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4581     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4582     if (!desktop) return FALSE;
4584     if (!g_object_get_data (tbl, "esc-pressed")) {
4585         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4586         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4587         sp_text_toolbox_size_changed (cbox, tbl);
4588         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4589     }
4590     return FALSE; // I consumed the event
4594 gboolean
4595 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4597     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4598     if (!desktop) return FALSE;
4600     switch (get_group0_keyval (event)) {
4601         case GDK_Escape: // defocus
4602             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4603             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4604             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4605             return TRUE; // I consumed the event
4606             break;
4607         case GDK_Return: // defocus
4608         case GDK_KP_Enter:
4609             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4610             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4611             sp_text_toolbox_size_changed (cbox, tbl);
4612             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4613             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4614             return TRUE; // I consumed the event
4615             break;
4616     }
4617     return FALSE;
4620 void
4621 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4622                                          GObject            *tbl)
4624     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4625     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4626     int x, y;
4628     if (!popdown_visible)
4629     {
4630         gdk_window_get_origin (widget->window, &x, &y);
4631         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4632         gtk_widget_show_all (popdown);
4633         //sp_transientize (popdown);
4635         gdk_pointer_grab (widget->window, TRUE,
4636                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4637                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4638                                         GDK_POINTER_MOTION_MASK),
4639                           NULL, NULL, GDK_CURRENT_TIME);
4641         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4643         popdown_visible = true;
4644     }
4645     else
4646     {
4647         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4648         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4649         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4650         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4651         gtk_widget_hide (popdown);
4652         popdown_visible = false;
4653     }
4656 gboolean
4657 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4658                                  GdkEventFocus    */*event*/,
4659                                  GObject          */*tbl*/)
4661     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4662     return FALSE;
4665 gboolean
4666 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4667                                    GdkEventFocus    */*event*/,
4668                                    GObject          */*tbl*/)
4670     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4672     if (popdown_hasfocus) {
4673         gtk_widget_hide (popdown);
4674         popdown_hasfocus = false;
4675         popdown_visible = false;
4676         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4677         return TRUE;
4678     }
4679     return FALSE;
4682 gboolean
4683 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
4684                                    GdkEventFocus    */*event*/,
4685                                    GObject          */*tbl*/)
4687     popdown_hasfocus = true;
4688     return TRUE;
4692 void
4693 cell_data_func  (GtkTreeViewColumn */*column*/,
4694                  GtkCellRenderer   *cell,
4695                  GtkTreeModel      *tree_model,
4696                  GtkTreeIter       *iter,
4697                  gpointer           /*data*/)
4699     char        *family,
4700         *family_escaped,
4701         *sample_escaped;
4703     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4705     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4707     family_escaped = g_markup_escape_text (family, -1);
4708     sample_escaped = g_markup_escape_text (sample, -1);
4710     std::stringstream markup;
4711     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4712     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4714     free (family);
4715     free (family_escaped);
4716     free (sample_escaped);
4719 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4720     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4721     if (completion) {
4722         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4723         g_object_unref (completion);
4724     }
4727 GtkWidget*
4728 sp_text_toolbox_new (SPDesktop *desktop)
4730     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4732     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4733     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4735     GtkTooltips *tt = gtk_tooltips_new();
4736     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4738     ////////////Family
4739     //Window
4740     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4741     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4743     //Entry
4744     GtkWidget           *entry = gtk_entry_new ();
4745     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4746     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4747     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4748     gtk_entry_completion_set_text_column (completion, 0);
4749     gtk_entry_completion_set_minimum_key_length (completion, 1);
4750     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4751     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4752     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4753     aux_toolbox_space (tbl, 1);
4754     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4755     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4757     //Button
4758     GtkWidget   *button = gtk_button_new ();
4759     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4760     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4762     //Popdown
4763     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4764     GtkWidget           *treeview = gtk_tree_view_new ();
4766     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4767     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4768     gtk_tree_view_column_pack_start (column, cell, FALSE);
4769     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4770     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4771     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4773     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4774     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4775     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4777     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4779     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4780     gtk_container_add (GTK_CONTAINER (sw), treeview);
4782     gtk_container_add (GTK_CONTAINER (window), sw);
4783     gtk_widget_set_size_request (window, 300, 450);
4785     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4786     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4787     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4789     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4791     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4792     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
4793     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4795     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4796     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4798     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4799     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4800     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4801     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4802     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4804     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4805     aux_toolbox_space (tbl, 1);
4806     GtkWidget *box = gtk_event_box_new ();
4807     gtk_container_add (GTK_CONTAINER (box), image);
4808     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4809     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4810     GtkTooltips *tooltips = gtk_tooltips_new ();
4811     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4812     gtk_widget_hide (GTK_WIDGET (box));
4813     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4815     ////////////Size
4816     const char *sizes[] = {
4817         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4818         "16", "18", "20", "22", "24", "28",
4819         "32", "36", "40", "48", "56", "64", "72", "144"
4820     };
4822     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4823     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4824     gtk_widget_set_size_request (cbox, 80, -1);
4825     aux_toolbox_space (tbl, 1);
4826     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4827     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4828     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4829     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4830     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4832     //spacer
4833     aux_toolbox_space (tbl, 4);
4834     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4836     ////////////Text anchor
4837     GtkWidget *group   = gtk_radio_button_new (NULL);
4838     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4839     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4841     // left
4842     GtkWidget *rbutton = group;
4843     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4844     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4845     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4847     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4848     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4849     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4850     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4852     // center
4853     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4854     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4855     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4856     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4858     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4859     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4860     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4861     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4863     // right
4864     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4865     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4866     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4867     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4869     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4870     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4871     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4872     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4874     // fill
4875     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4876     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4877     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4878     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4880     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4881     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4882     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4883     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4885     aux_toolbox_space (tbl, 1);
4886     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4888     //spacer
4889     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4891     ////////////Text style
4892     row = gtk_hbox_new (FALSE, 4);
4894     // bold
4895     rbutton = gtk_toggle_button_new ();
4896     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4897     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4898     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4899     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4901     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4902     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4903     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4905     // italic
4906     rbutton = gtk_toggle_button_new ();
4907     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4908     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4909     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4910     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4912     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4913     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4914     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4916     aux_toolbox_space (tbl, 1);
4917     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4919     //spacer
4920     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4922     ////////////Text orientation
4923     group   = gtk_radio_button_new (NULL);
4924     row     = gtk_hbox_new (FALSE, 4);
4925     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4927     // horizontal
4928     rbutton = group;
4929     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4930     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4931     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4932     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4934     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4935     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4936     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4938     // vertical
4939     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4940     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4941     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4942     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4943     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4945     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4946     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4947     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4948     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4951     //watch selection
4952     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4954     sigc::connection *c_selection_changed =
4955         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4956                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4957     pool->add_connection ("selection-changed", c_selection_changed);
4959     sigc::connection *c_selection_modified =
4960         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4961                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4962     pool->add_connection ("selection-modified", c_selection_modified);
4964     sigc::connection *c_subselection_changed =
4965         new sigc::connection (desktop->connectToolSubselectionChanged
4966                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4967     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4969     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4972     gtk_widget_show_all (tbl);
4973     return tbl;
4975 } // end of sp_text_toolbox_new()
4977 }//<unnamed> namespace
4980 //#########################
4981 //##      Connector      ##
4982 //#########################
4984 static void sp_connector_path_set_avoid(void)
4986     cc_selection_set_avoid(true);
4990 static void sp_connector_path_set_ignore(void)
4992     cc_selection_set_avoid(false);
4997 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4999     // quit if run by the _changed callbacks
5000     if (g_object_get_data( tbl, "freeze" )) {
5001         return;
5002     }
5004     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5005     SPDocument *doc = sp_desktop_document(desktop);
5007     if (!sp_document_get_undo_sensitive(doc))
5008     {
5009         return;
5010     }
5012     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5014     if ( repr->attribute("inkscape:connector-spacing") ) {
5015         gdouble priorValue = gtk_adjustment_get_value(adj);
5016         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5017         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5018             return;
5019         }
5020     } else if ( adj->value == defaultConnSpacing ) {
5021         return;
5022     }
5024     // in turn, prevent callbacks from responding
5025     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5027     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5028     SP_OBJECT(desktop->namedview)->updateRepr();
5030     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5031     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5032         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5033         NR::Matrix m = NR::identity();
5034         avoid_item_move(&m, item);
5035     }
5037     if (items) {
5038         g_slist_free(items);
5039     }
5041     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5042             _("Change connector spacing"));
5044     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5046     spinbutton_defocus(GTK_OBJECT(tbl));
5049 static void sp_connector_graph_layout(void)
5051     if (!SP_ACTIVE_DESKTOP) return;
5053     // hack for clones, see comment in align-and-distribute.cpp
5054     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5055     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5057     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5059     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5061     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5064 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5066     if ( gtk_toggle_action_get_active( act ) ) {
5067         prefs_set_string_attribute("tools.connector", "directedlayout",
5068                 "true");
5069     } else {
5070         prefs_set_string_attribute("tools.connector", "directedlayout",
5071                 "false");
5072     }
5075 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5077     if ( gtk_toggle_action_get_active( act ) ) {
5078         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5079                 "true");
5080     } else {
5081         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5082                 "false");
5083     }
5087 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5089     prefs_set_double_attribute("tools.connector", "length", adj->value);
5090     spinbutton_defocus(GTK_OBJECT(tbl));
5093 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5094                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5095                                             bool /*is_interactive*/, gpointer data)
5097     GtkWidget *tbl = GTK_WIDGET(data);
5099     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5100         return;
5101     }
5102     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5103         return;
5104     }
5106     GtkAdjustment *adj = (GtkAdjustment*)
5107             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5108     gdouble spacing = defaultConnSpacing;
5109     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5111     gtk_adjustment_set_value(adj, spacing);
5115 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5116     NULL, /* child_added */
5117     NULL, /* child_removed */
5118     connector_tb_event_attr_changed,
5119     NULL, /* content_changed */
5120     NULL  /* order_changed */
5121 };
5124 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5126     {
5127         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5128                                           _("Avoid"),
5129                                           _("Make connectors avoid selected objects"),
5130                                           "connector_avoid",
5131                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5132         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5133         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5134     }
5136     {
5137         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5138                                           _("Ignore"),
5139                                           _("Make connectors ignore selected objects"),
5140                                           "connector_ignore",
5141                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5142         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5143         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5144     }
5146     EgeAdjustmentAction* eact = 0;
5148     // Spacing spinbox
5149     eact = create_adjustment_action( "ConnectorSpacingAction",
5150                                      _("Connector Spacing"), _("Spacing:"),
5151                                      _("The amount of space left around objects by auto-routing connectors"),
5152                                      "tools.connector", "spacing", defaultConnSpacing,
5153                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5154                                      0, 100, 1.0, 10.0,
5155                                      0, 0, 0,
5156                                      connector_spacing_changed, 1, 0 );
5157     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5159     // Graph (connector network) layout
5160     {
5161         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5162                                           _("Graph"),
5163                                           _("Nicely arrange selected connector network"),
5164                                           "graph_layout",
5165                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5166         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5167         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5168     }
5170     // Default connector length spinbox
5171     eact = create_adjustment_action( "ConnectorLengthAction",
5172                                      _("Connector Length"), _("Length:"),
5173                                      _("Ideal length for connectors when layout is applied"),
5174                                      "tools.connector", "length", 100,
5175                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5176                                      10, 1000, 10.0, 100.0,
5177                                      0, 0, 0,
5178                                      connector_length_changed, 1, 0 );
5179     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5182     // Directed edges toggle button
5183     {
5184         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5185                                                       _("Downwards"),
5186                                                       _("Make connectors with end-markers (arrows) point downwards"),
5187                                                       "directed_graph",
5188                                                       Inkscape::ICON_SIZE_DECORATION );
5189         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5191         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5192         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5193                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5195         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5196     }
5198     // Avoid overlaps toggle button
5199     {
5200         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5201                                                       _("Remove overlaps"),
5202                                                       _("Do not allow overlapping shapes"),
5203                                                       "remove_overlaps",
5204                                                       Inkscape::ICON_SIZE_DECORATION );
5205         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5207         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5208         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5209                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5211         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5212     }
5214     // Code to watch for changes to the connector-spacing attribute in
5215     // the XML.
5216     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5217     g_assert(repr != NULL);
5219     purge_repr_listener( holder, holder );
5221     if (repr) {
5222         g_object_set_data( holder, "repr", repr );
5223         Inkscape::GC::anchor(repr);
5224         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5225         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5226     }
5227 } // end of sp_connector_toolbox_prep()
5230 //#########################
5231 //##     Paintbucket     ##
5232 //#########################
5234 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5236     gint channels = ege_select_one_action_get_active( act );
5237     flood_channels_set_channels( channels );
5240 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5242     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5245 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5247     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5250 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5252     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5253     SPUnit const *unit = tracker->getActiveUnit();
5255     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5257     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5260 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5262     // FIXME: make defaults settable via Inkscape Options
5263     struct KeyValue {
5264         char const *key;
5265         double value;
5266     } const key_values[] = {
5267         {"threshold", 15},
5268         {"offset", 0.0}
5269     };
5271     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5272         KeyValue const &kv = key_values[i];
5273         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5274         if ( adj ) {
5275             gtk_adjustment_set_value(adj, kv.value);
5276         }
5277     }
5279     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5280     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5281     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5282     ege_select_one_action_set_active( autogap_action, 0 );
5285 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5287     EgeAdjustmentAction* eact = 0;
5289     {
5290         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5292         GList* items = 0;
5293         gint count = 0;
5294         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5295         {
5296             GtkTreeIter iter;
5297             gtk_list_store_append( model, &iter );
5298             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5299             count++;
5300         }
5301         g_list_free( items );
5302         items = 0;
5303         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5304         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5305         ege_select_one_action_set_appearance( act1, "compact" );
5306         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5307         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5308         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5309         g_object_set_data( holder, "channels_action", act1 );
5310     }
5312     // Spacing spinbox
5313     {
5314         eact = create_adjustment_action(
5315             "ThresholdAction",
5316             _("Fill Threshold"), _("Threshold:"),
5317             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5318             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5319             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5320             0, 0, 0,
5321             paintbucket_threshold_changed, 1, 0 );
5323         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5324     }
5326     // Create the units menu.
5327     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5328     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5329     if (stored_unit)
5330         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5331     g_object_set_data( holder, "tracker", tracker );
5332     {
5333         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5334         gtk_action_group_add_action( mainActions, act );
5335     }
5337     // Offset spinbox
5338     {
5339         eact = create_adjustment_action(
5340             "OffsetAction",
5341             _("Grow/shrink by"), _("Grow/shrink by:"),
5342             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5343             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5344             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5345             0, 0, 0,
5346             paintbucket_offset_changed, 1, 2);
5347         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5349         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5350     }
5352     /* Auto Gap */
5353     {
5354         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5356         GList* items = 0;
5357         gint count = 0;
5358         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5359         {
5360             GtkTreeIter iter;
5361             gtk_list_store_append( model, &iter );
5362             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5363             count++;
5364         }
5365         g_list_free( items );
5366         items = 0;
5367         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5368         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5369         ege_select_one_action_set_appearance( act2, "compact" );
5370         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5371         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5372         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5373         g_object_set_data( holder, "autogap_action", act2 );
5374     }
5376     /* Reset */
5377     {
5378         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5379                                           _("Defaults"),
5380                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5381                                           GTK_STOCK_CLEAR );
5382         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5383         gtk_action_group_add_action( mainActions, act );
5384         gtk_action_set_sensitive( act, TRUE );
5385     }
5389 /*
5390   Local Variables:
5391   mode:c++
5392   c-file-style:"stroustrup"
5393   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5394   indent-tabs-mode:nil
5395   fill-column:99
5396   End:
5397 */
5398 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :