Code

Applying fixes for gcc 4.3 build issues (closes LP: #169115)
[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"
44 #include "ui/widget/style-swatch.h"
46 #include "prefs-utils.h"
47 #include "verbs.h"
48 #include "sp-namedview.h"
49 #include "desktop.h"
50 #include "desktop-handles.h"
51 #include "xml/repr.h"
52 #include "xml/node-event-vector.h"
53 #include <glibmm/i18n.h>
54 #include "helper/unit-menu.h"
55 #include "helper/units.h"
57 #include "inkscape.h"
58 #include "conn-avoid-ref.h"
61 #include "select-toolbar.h"
62 #include "gradient-toolbar.h"
64 #include "connector-context.h"
65 #include "node-context.h"
66 #include "shape-editor.h"
67 #include "tweak-context.h"
68 #include "sp-rect.h"
69 #include "box3d.h"
70 #include "box3d-context.h"
71 #include "sp-star.h"
72 #include "sp-spiral.h"
73 #include "sp-ellipse.h"
74 #include "sp-text.h"
75 #include "sp-flowtext.h"
76 #include "style.h"
77 #include "selection.h"
78 #include "selection-chemistry.h"
79 #include "document-private.h"
80 #include "desktop-style.h"
81 #include "../libnrtype/font-lister.h"
82 #include "../libnrtype/font-instance.h"
83 #include "../connection-pool.h"
84 #include "../prefs-utils.h"
85 #include "../inkscape-stock.h"
86 #include "icon.h"
87 #include "graphlayout/graphlayout.h"
89 #include "mod360.h"
91 #include "toolbox.h"
93 #include "flood-context.h"
95 #include "ink-action.h"
96 #include "ege-adjustment-action.h"
97 #include "ege-output-action.h"
98 #include "ege-select-one-action.h"
99 #include "helper/unit-tracker.h"
101 #include "svg/css-ostringstream.h"
103 using Inkscape::UnitTracker;
105 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
106 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
108 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
121 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
127 static struct {
128     gchar const *type_name;
129     gchar const *data_name;
130     sp_verb_t verb;
131     sp_verb_t doubleclick_verb;
132 } const tools[] = {
133     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
134     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
135     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
136     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
137     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
138     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
139     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
140     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
141     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
142     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
143     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
144     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
145     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
146     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
147     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
148     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
149     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
150     { NULL, NULL, 0, 0 }
151 };
153 static struct {
154     gchar const *type_name;
155     gchar const *data_name;
156     GtkWidget *(*create_func)(SPDesktop *desktop);
157     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
158     gchar const *ui_name;
159     gint swatch_verb_id;
160     gchar const *swatch_tool;
161     gchar const *swatch_tip;
162 } const aux_toolboxes[] = {
163     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
164       SP_VERB_INVALID, 0, 0},
165     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
166       SP_VERB_INVALID, 0, 0},
167     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
168       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
169     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
170       SP_VERB_INVALID, 0, 0},
171     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
172       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
173     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
174       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
175     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
176       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
177     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
178       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
179     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
180       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
181     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
182       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
183     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
184       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
185     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
186       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
187     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
188       SP_VERB_INVALID, 0, 0},
189     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
190       SP_VERB_INVALID, 0, 0},
191     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
192       SP_VERB_INVALID, 0, 0},
193     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
194       SP_VERB_INVALID, 0, 0},
195     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
196       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
197     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
198 };
201 static gchar const * ui_descr =
202         "<ui>"
203         "  <toolbar name='SelectToolbar'>"
204         "    <toolitem action='EditSelectAll' />"
205         "    <toolitem action='EditSelectAllInAllLayers' />"
206         "    <toolitem action='EditDeselect' />"
207         "    <separator />"
208         "    <toolitem action='ObjectRotate90CCW' />"
209         "    <toolitem action='ObjectRotate90' />"
210         "    <toolitem action='ObjectFlipHorizontally' />"
211         "    <toolitem action='ObjectFlipVertically' />"
212         "    <separator />"
213         "    <toolitem action='SelectionToBack' />"
214         "    <toolitem action='SelectionLower' />"
215         "    <toolitem action='SelectionRaise' />"
216         "    <toolitem action='SelectionToFront' />"
217         "    <separator />"
218         "    <toolitem action='XAction' />"
219         "    <toolitem action='YAction' />"
220         "    <toolitem action='WidthAction' />"
221         "    <toolitem action='LockAction' />"
222         "    <toolitem action='HeightAction' />"
223         "    <toolitem action='UnitsAction' />"
224         "    <separator />"
225         "    <toolitem action='transform_affect_label' />"
226         "    <toolitem action='transform_stroke' />"
227         "    <toolitem action='transform_corners' />"
228         "    <toolitem action='transform_gradient' />"
229         "    <toolitem action='transform_pattern' />"
230         "  </toolbar>"
232         "  <toolbar name='NodeToolbar'>"
233         "    <toolitem action='NodeInsertAction' />"
234         "    <toolitem action='NodeDeleteAction' />"
235         "    <separator />"
236         "    <toolitem action='NodeJoinAction' />"
237         "    <toolitem action='NodeJoinSegmentAction' />"
238         "    <toolitem action='NodeDeleteSegmentAction' />"
239         "    <toolitem action='NodeBreakAction' />"
240         "    <separator />"
241         "    <toolitem action='NodeCuspAction' />"
242         "    <toolitem action='NodeSmoothAction' />"
243         "    <toolitem action='NodeSymmetricAction' />"
244         "    <separator />"
245         "    <toolitem action='NodeLineAction' />"
246         "    <toolitem action='NodeCurveAction' />"
247         "    <separator />"
248         "    <toolitem action='ObjectToPath' />"
249         "    <toolitem action='StrokeToPath' />"
250         "    <separator />"
251         "    <toolitem action='NodesShowHandlesAction' />"
252         "    <separator />"
253         "    <toolitem action='EditNextLPEParameterAction' />"
254         "    <separator />"
255         "    <toolitem action='NodeXAction' />"
256         "    <toolitem action='NodeYAction' />"
257         "    <toolitem action='NodeUnitsAction' />"
258         "  </toolbar>"
260         "  <toolbar name='TweakToolbar'>"
261         "    <toolitem action='TweakWidthAction' />"
262         "    <separator />"
263         "    <toolitem action='TweakForceAction' />"
264         "    <toolitem action='TweakPressureAction' />"
265         "    <separator />"
266         "    <toolitem action='TweakModeAction' />"
267         "    <separator />"
268         "    <toolitem action='TweakFidelityAction' />"
269         "    <separator />"
270         "    <toolitem action='TweakChannelsLabel' />"
271         "    <toolitem action='TweakDoH' />"
272         "    <toolitem action='TweakDoS' />"
273         "    <toolitem action='TweakDoL' />"
274         "    <toolitem action='TweakDoO' />"
275         "  </toolbar>"
277         "  <toolbar name='ZoomToolbar'>"
278         "    <toolitem action='ZoomIn' />"
279         "    <toolitem action='ZoomOut' />"
280         "    <separator />"
281         "    <toolitem action='Zoom1:0' />"
282         "    <toolitem action='Zoom1:2' />"
283         "    <toolitem action='Zoom2:1' />"
284         "    <separator />"
285         "    <toolitem action='ZoomSelection' />"
286         "    <toolitem action='ZoomDrawing' />"
287         "    <toolitem action='ZoomPage' />"
288         "    <toolitem action='ZoomPageWidth' />"
289         "    <separator />"
290         "    <toolitem action='ZoomPrev' />"
291         "    <toolitem action='ZoomNext' />"
292         "  </toolbar>"
294         "  <toolbar name='StarToolbar'>"
295         "    <separator />"
296         "    <toolitem action='StarStateAction' />"
297         "    <separator />"
298         "    <toolitem action='FlatAction' />"
299         "    <separator />"
300         "    <toolitem action='MagnitudeAction' />"
301         "    <toolitem action='SpokeAction' />"
302         "    <toolitem action='RoundednessAction' />"
303         "    <toolitem action='RandomizationAction' />"
304         "    <separator />"
305         "    <toolitem action='StarResetAction' />"
306         "  </toolbar>"
308         "  <toolbar name='RectToolbar'>"
309         "    <toolitem action='RectStateAction' />"
310         "    <toolitem action='RectWidthAction' />"
311         "    <toolitem action='RectHeightAction' />"
312         "    <toolitem action='RadiusXAction' />"
313         "    <toolitem action='RadiusYAction' />"
314         "    <toolitem action='RectUnitsAction' />"
315         "    <separator />"
316         "    <toolitem action='RectResetAction' />"
317         "  </toolbar>"
319         "  <toolbar name='3DBoxToolbar'>"
320         "    <toolitem action='3DBoxAngleXAction' />"
321         "    <toolitem action='3DBoxVPXStateAction' />"
322         "    <separator />"
323         "    <toolitem action='3DBoxAngleYAction' />"
324         "    <toolitem action='3DBoxVPYStateAction' />"
325         "    <separator />"
326         "    <toolitem action='3DBoxAngleZAction' />"
327         "    <toolitem action='3DBoxVPZStateAction' />"
328         "  </toolbar>"
330         "  <toolbar name='SpiralToolbar'>"
331         "    <toolitem action='SpiralStateAction' />"
332         "    <toolitem action='SpiralRevolutionAction' />"
333         "    <toolitem action='SpiralExpansionAction' />"
334         "    <toolitem action='SpiralT0Action' />"
335         "    <separator />"
336         "    <toolitem action='SpiralResetAction' />"
337         "  </toolbar>"
339         "  <toolbar name='PenToolbar'>"
340         "  </toolbar>"
342         "  <toolbar name='PencilToolbar'>"
343         "  </toolbar>"
345         "  <toolbar name='CalligraphyToolbar'>"
346         "    <separator />"
347         "    <toolitem action='CalligraphyWidthAction' />"
348         "    <toolitem action='PressureAction' />"
349         "    <toolitem action='TraceAction' />"
350         "    <toolitem action='ThinningAction' />"
351         "    <separator />"
352         "    <toolitem action='AngleAction' />"
353         "    <toolitem action='TiltAction' />"
354         "    <toolitem action='FixationAction' />"
355         "    <separator />"
356         "    <toolitem action='CapRoundingAction' />"
357         "    <separator />"
358         "    <toolitem action='TremorAction' />"
359         "    <toolitem action='WiggleAction' />"
360         "    <toolitem action='MassAction' />"
361         "    <separator />"
362         "    <toolitem action='CalligraphyResetAction' />"
363         "  </toolbar>"
365         "  <toolbar name='ArcToolbar'>"
366         "    <toolitem action='ArcStateAction' />"
367         "    <separator />"
368         "    <toolitem action='ArcStartAction' />"
369         "    <toolitem action='ArcEndAction' />"
370         "    <separator />"
371         "    <toolitem action='ArcOpenAction' />"
372         "    <separator />"
373         "    <toolitem action='ArcResetAction' />"
374         "    <separator />"
375         "  </toolbar>"
377         "  <toolbar name='PaintbucketToolbar'>"
378         "    <toolitem action='ChannelsAction' />"
379         "    <separator />"
380         "    <toolitem action='ThresholdAction' />"
381         "    <separator />"
382         "    <toolitem action='OffsetAction' />"
383         "    <toolitem action='PaintbucketUnitsAction' />"
384         "    <separator />"
385         "    <toolitem action='AutoGapAction' />"
386         "    <separator />"
387         "    <toolitem action='PaintbucketResetAction' />"
388         "  </toolbar>"
390         "  <toolbar name='DropperToolbar'>"
391         "    <toolitem action='DropperPickAlphaAction' />"
392         "    <toolitem action='DropperSetAlphaAction' />"
393         "  </toolbar>"
395         "  <toolbar name='ConnectorToolbar'>"
396         "    <toolitem action='ConnectorAvoidAction' />"
397         "    <toolitem action='ConnectorIgnoreAction' />"
398         "    <toolitem action='ConnectorSpacingAction' />"
399         "    <toolitem action='ConnectorGraphAction' />"
400         "    <toolitem action='ConnectorLengthAction' />"
401         "    <toolitem action='ConnectorDirectedAction' />"
402         "    <toolitem action='ConnectorOverlapAction' />"
403         "  </toolbar>"
405         "</ui>"
408 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
410 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
412 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
413 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
415 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
416 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
418 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
419 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
421 /* Global text entry widgets necessary for update */
422 /* GtkWidget *dropper_rgb_entry,
423           *dropper_opacity_entry ; */
424 // should be made a private member once this is converted to class
426 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
427     connection->disconnect();
428     delete connection;
431 static void purge_repr_listener( GObject* obj, GObject* tbl )
433     (void)obj;
434     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
435     if (oldrepr) { // remove old listener
436         sp_repr_remove_listener_by_data(oldrepr, tbl);
437         Inkscape::GC::release(oldrepr);
438         oldrepr = 0;
439         g_object_set_data( tbl, "repr", NULL );
440     }
443 GtkWidget *
444 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
445                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
446                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
448     SPAction *action = verb->get_action(view);
449     if (!action) return NULL;
451     SPAction *doubleclick_action;
452     if (doubleclick_verb)
453         doubleclick_action = doubleclick_verb->get_action(view);
454     else
455         doubleclick_action = NULL;
457     /* fixme: Handle sensitive/unsensitive */
458     /* fixme: Implement sp_button_new_from_action */
459     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
460     gtk_widget_show(b);
461     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
463     return b;
466 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
467                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
469     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
472 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
473                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
475     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
479 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
481     SPAction* targetAction = SP_ACTION(user_data);
482     if ( targetAction ) {
483         sp_action_perform( targetAction, NULL );
484     }
487 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
489     if ( data ) {
490         GtkAction* act = GTK_ACTION(data);
491         gtk_action_set_sensitive( act, sensitive );
492     }
495 static SPActionEventVector action_event_vector = {
496     {NULL},
497     NULL,
498     NULL,
499     sp_action_action_set_sensitive,
500     NULL,
501     NULL
502 };
504 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
506     GtkAction* act = 0;
508     SPAction* targetAction = verb->get_action(view);
509     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
510     act = GTK_ACTION(inky);
511     gtk_action_set_sensitive( act, targetAction->sensitive );
513     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
515     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
516     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
518     return act;
521 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
523     Inkscape::UI::View::View *view = desktop;
524     gint verbsToUse[] = {
525         // disabled until we have icons for them:
526         //find
527         //SP_VERB_EDIT_TILE,
528         //SP_VERB_EDIT_UNTILE,
529         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
530         SP_VERB_DIALOG_DISPLAY,
531         SP_VERB_DIALOG_FILL_STROKE,
532         SP_VERB_DIALOG_NAMEDVIEW,
533         SP_VERB_DIALOG_TEXT,
534         SP_VERB_DIALOG_XML_EDITOR,
535         SP_VERB_EDIT_CLONE,
536         SP_VERB_EDIT_COPY,
537         SP_VERB_EDIT_CUT,
538         SP_VERB_EDIT_DUPLICATE,
539         SP_VERB_EDIT_PASTE,
540         SP_VERB_EDIT_REDO,
541         SP_VERB_EDIT_UNDO,
542         SP_VERB_EDIT_UNLINK_CLONE,
543         SP_VERB_FILE_EXPORT,
544         SP_VERB_FILE_IMPORT,
545         SP_VERB_FILE_NEW,
546         SP_VERB_FILE_OPEN,
547         SP_VERB_FILE_PRINT,
548         SP_VERB_FILE_SAVE,
549         SP_VERB_OBJECT_TO_CURVE,
550         SP_VERB_SELECTION_GROUP,
551         SP_VERB_SELECTION_OUTLINE,
552         SP_VERB_SELECTION_UNGROUP,
553         SP_VERB_ZOOM_1_1,
554         SP_VERB_ZOOM_1_2,
555         SP_VERB_ZOOM_2_1,
556         SP_VERB_ZOOM_DRAWING,
557         SP_VERB_ZOOM_IN,
558         SP_VERB_ZOOM_NEXT,
559         SP_VERB_ZOOM_OUT,
560         SP_VERB_ZOOM_PAGE,
561         SP_VERB_ZOOM_PAGE_WIDTH,
562         SP_VERB_ZOOM_PREV,
563         SP_VERB_ZOOM_SELECTION,
564     };
566     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
567     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
569     static std::map<SPDesktop*, GtkActionGroup*> groups;
570     GtkActionGroup* mainActions = 0;
571     if ( groups.find(desktop) != groups.end() ) {
572         mainActions = groups[desktop];
573     }
575     if ( !mainActions ) {
576         mainActions = gtk_action_group_new("main");
577         groups[desktop] = mainActions;
578     }
580     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
581         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
582         if ( verb ) {
583             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
584                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
585                 gtk_action_group_add_action( mainActions, act );
586             }
587         }
588     }
590     return mainActions;
594 GtkWidget *
595 sp_tool_toolbox_new()
597     GtkTooltips *tt = gtk_tooltips_new();
598     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
600     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
601     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
603     gtk_widget_set_sensitive(tb, FALSE);
605     GtkWidget *hb = gtk_handle_box_new();
606     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
607     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
608     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
610     gtk_container_add(GTK_CONTAINER(hb), tb);
611     gtk_widget_show(GTK_WIDGET(tb));
613     sigc::connection* conn = new sigc::connection;
614     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
616     return hb;
619 static void
620 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
622     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
623     gtk_widget_queue_resize(child);
626 static void
627 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
629     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
630     gtk_widget_queue_resize(child);
633 GtkWidget *
634 sp_aux_toolbox_new()
636     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
638     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
640     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
642     gtk_widget_set_sensitive(tb, FALSE);
644     GtkWidget *hb = gtk_handle_box_new();
645     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
646     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
647     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
649     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
650     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
652     gtk_container_add(GTK_CONTAINER(hb), tb);
653     gtk_widget_show(GTK_WIDGET(tb));
655     sigc::connection* conn = new sigc::connection;
656     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
658     return hb;
661 //####################################
662 //# Commands Bar
663 //####################################
665 GtkWidget *
666 sp_commands_toolbox_new()
668     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
670     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
672     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
673     gtk_widget_set_sensitive(tb, FALSE);
675     GtkWidget *hb = gtk_handle_box_new();
676     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
677     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
678     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
680     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
681     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
683     gtk_container_add(GTK_CONTAINER(hb), tb);
684     gtk_widget_show(GTK_WIDGET(tb));
686     sigc::connection* conn = new sigc::connection;
687     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
689     return hb;
692 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
693                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
694                                                        gchar const *path, gchar const *data, gdouble def,
695                                                        GtkWidget *focusTarget,
696                                                        GtkWidget *us,
697                                                        GObject *dataKludge,
698                                                        gboolean altx, gchar const *altx_mark,
699                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
700                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
701                                                        void (*callback)(GtkAdjustment *, GObject *),
702                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
704     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
705                                                              lower, upper, step, page, page ) );
706     if (us) {
707         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
708     }
710     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
712     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
713     if ( shortLabel ) {
714         g_object_set( act, "short_label", shortLabel, NULL );
715     }
717     if ( (descrCount > 0) && descrLabels && descrValues ) {
718         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
719     }
721     if ( focusTarget ) {
722         ege_adjustment_action_set_focuswidget( act, focusTarget );
723     }
725     if ( altx && altx_mark ) {
726         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
727     }
729     if ( dataKludge ) {
730         g_object_set_data( dataKludge, data, adj );
731     }
733     // Using a cast just to make sure we pass in the right kind of function pointer
734     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
736     return act;
740 //####################################
741 //# node editing callbacks
742 //####################################
744 /**
745  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
746  */
747 static ShapeEditor *get_current_shape_editor()
749     if (!SP_ACTIVE_DESKTOP) {
750         return NULL;
751     }
753     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
755     if (!SP_IS_NODE_CONTEXT(event_context)) {
756         return NULL;
757     }
759     return SP_NODE_CONTEXT(event_context)->shape_editor;
763 void
764 sp_node_path_edit_add(void)
766     ShapeEditor *shape_editor = get_current_shape_editor();
767     if (shape_editor) shape_editor->add_node();
770 void
771 sp_node_path_edit_delete(void)
773     ShapeEditor *shape_editor = get_current_shape_editor();
774     if (shape_editor) shape_editor->delete_nodes();
777 void
778 sp_node_path_edit_delete_segment(void)
780     ShapeEditor *shape_editor = get_current_shape_editor();
781     if (shape_editor) shape_editor->delete_segment();
784 void
785 sp_node_path_edit_break(void)
787     ShapeEditor *shape_editor = get_current_shape_editor();
788     if (shape_editor) shape_editor->break_at_nodes();
791 void
792 sp_node_path_edit_join(void)
794     ShapeEditor *shape_editor = get_current_shape_editor();
795     if (shape_editor) shape_editor->join_nodes();
798 void
799 sp_node_path_edit_join_segment(void)
801     ShapeEditor *shape_editor = get_current_shape_editor();
802     if (shape_editor) shape_editor->join_segments();
805 void
806 sp_node_path_edit_toline(void)
808     ShapeEditor *shape_editor = get_current_shape_editor();
809     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
812 void
813 sp_node_path_edit_tocurve(void)
815     ShapeEditor *shape_editor = get_current_shape_editor();
816     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
819 void
820 sp_node_path_edit_cusp(void)
822     ShapeEditor *shape_editor = get_current_shape_editor();
823     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
826 void
827 sp_node_path_edit_smooth(void)
829     ShapeEditor *shape_editor = get_current_shape_editor();
830     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
833 void
834 sp_node_path_edit_symmetrical(void)
836     ShapeEditor *shape_editor = get_current_shape_editor();
837     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
840 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
841     bool show = gtk_toggle_action_get_active( act );
842     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
843     ShapeEditor *shape_editor = get_current_shape_editor();
844     if (shape_editor) shape_editor->show_handles(show);
847 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
848     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
851 /* is called when the node selection is modified */
852 static void
853 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
855     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
856     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
857     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
858     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
860     // quit if run by the attr_changed listener
861     if (g_object_get_data( tbl, "freeze" )) {
862         return;
863     }
865     // in turn, prevent listener from responding
866     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
868     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
869     SPUnit const *unit = tracker->getActiveUnit();
871     ShapeEditor *shape_editor = get_current_shape_editor();
872     if (shape_editor && shape_editor->has_nodepath()) {
873         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
874         int n_selected = 0;
875         if (nodepath) {
876             n_selected = nodepath->numSelected();
877         }
879         if (n_selected == 0) {
880             gtk_action_set_sensitive(xact, FALSE);
881             gtk_action_set_sensitive(yact, FALSE);
882         } else {
883             gtk_action_set_sensitive(xact, TRUE);
884             gtk_action_set_sensitive(yact, TRUE);
885             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
886             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
888             if (n_selected == 1) {
889                 NR::Point sel_node = nodepath->singleSelectedCoords();
890                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
891                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
892                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
893                 }
894             } else {
895                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
896                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
897                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
898                     /* Note: Currently x and y will always have a value, even if the coordinates of the
899                        selected nodes don't coincide (in this case we use the coordinates of the center
900                        of the bounding box). So the entries are never set to zero. */
901                     // FIXME: Maybe we should clear the entry if several nodes are selected
902                     //        instead of providing a kind of average value
903                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
904                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
905                 }
906             }
907         }
908     } else {
909         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
910         gtk_action_set_sensitive(xact, FALSE);
911         gtk_action_set_sensitive(yact, FALSE);
912     }
914     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
917 static void
918 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
920     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
922     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
923     SPUnit const *unit = tracker->getActiveUnit();
925     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
926         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
927     }
929     // quit if run by the attr_changed listener
930     if (g_object_get_data( tbl, "freeze" )) {
931         return;
932     }
934     // in turn, prevent listener from responding
935     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
937     ShapeEditor *shape_editor = get_current_shape_editor();
938     if (shape_editor && shape_editor->has_nodepath()) {
939         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
940         if (!strcmp(value_name, "x")) {
941             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
942         }
943         if (!strcmp(value_name, "y")) {
944             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
945         }
946     }
948     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
951 static void
952 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
954     sp_node_path_value_changed(adj, tbl, "x");
957 static void
958 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
960     sp_node_path_value_changed(adj, tbl, "y");
963 //################################
964 //##    Node Editing Toolbox    ##
965 //################################
967 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
969     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
970     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
971     g_object_set_data( holder, "tracker", tracker );
973     {
974         InkAction* inky = ink_action_new( "NodeInsertAction",
975                                           _("Insert node"),
976                                           _("Insert new nodes into selected segments"),
977                                           "node_insert",
978                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
979         g_object_set( inky, "short_label", _("Insert"), NULL );
980         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
981         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
982     }
984     {
985         InkAction* inky = ink_action_new( "NodeDeleteAction",
986                                           _("Delete node"),
987                                           _("Delete selected nodes"),
988                                           "node_delete",
989                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
990         g_object_set( inky, "short_label", _("Delete"), NULL );
991         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
992         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
993     }
995     {
996         InkAction* inky = ink_action_new( "NodeJoinAction",
997                                           _("Join endnodes"),
998                                           _("Join selected endnodes"),
999                                           "node_join",
1000                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1001         g_object_set( inky, "short_label", _("Join"), NULL );
1002         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1003         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1004     }
1006     {
1007         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1008                                           _("Join Segment"),
1009                                           _("Join selected endnodes with a new segment"),
1010                                           "node_join_segment",
1011                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1012         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1013         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1014     }
1016     {
1017         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1018                                           _("Delete Segment"),
1019                                           _("Split path between two non-endpoint nodes"),
1020                                           "node_delete_segment",
1021                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1022         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1023         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1024     }
1026     {
1027         InkAction* inky = ink_action_new( "NodeBreakAction",
1028                                           _("Node Break"),
1029                                           _("Break path at selected nodes"),
1030                                           "node_break",
1031                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1032         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1033         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1034     }
1036     {
1037         InkAction* inky = ink_action_new( "NodeCuspAction",
1038                                           _("Node Cusp"),
1039                                           _("Make selected nodes corner"),
1040                                           "node_cusp",
1041                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1042         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1043         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1044     }
1046     {
1047         InkAction* inky = ink_action_new( "NodeSmoothAction",
1048                                           _("Node Smooth"),
1049                                           _("Make selected nodes smooth"),
1050                                           "node_smooth",
1051                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1052         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1053         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1054     }
1056     {
1057         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1058                                           _("Node Symmetric"),
1059                                           _("Make selected nodes symmetric"),
1060                                           "node_symmetric",
1061                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1062         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1063         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1064     }
1066     {
1067         InkAction* inky = ink_action_new( "NodeLineAction",
1068                                           _("Node Line"),
1069                                           _("Make selected segments lines"),
1070                                           "node_line",
1071                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1072         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1073         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1074     }
1076     {
1077         InkAction* inky = ink_action_new( "NodeCurveAction",
1078                                           _("Node Curve"),
1079                                           _("Make selected segments curves"),
1080                                           "node_curve",
1081                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1082         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1083         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1084     }
1086     {
1087         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1088                                                       _("Show Handles"),
1089                                                       _("Show the Bezier handles of selected nodes"),
1090                                                       "nodes_show_handles",
1091                                                       Inkscape::ICON_SIZE_DECORATION );
1092         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1093         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1094         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1095     }
1097     {
1098         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1099                                           _("Next Path Effect Parameter"),
1100                                           _("Show next Path Effect parameter for editing"),
1101                                           "edit_next_parameter",
1102                                           Inkscape::ICON_SIZE_DECORATION );
1103         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1104         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1105     }
1107     /* X coord of selected node(s) */
1108     {
1109         EgeAdjustmentAction* eact = 0;
1110         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1111         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1112         eact = create_adjustment_action( "NodeXAction",
1113                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1114                                          "tools.nodes", "Xcoord", 0,
1115                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1116                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1117                                          labels, values, G_N_ELEMENTS(labels),
1118                                          sp_node_path_x_value_changed );
1119         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1120         g_object_set_data( holder, "nodes_x_action", eact );
1121         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1122         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1123     }
1125     /* Y coord of selected node(s) */
1126     {
1127         EgeAdjustmentAction* eact = 0;
1128         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1129         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1130         eact = create_adjustment_action( "NodeYAction",
1131                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1132                                          "tools.nodes", "Ycoord", 0,
1133                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1134                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1135                                          labels, values, G_N_ELEMENTS(labels),
1136                                          sp_node_path_y_value_changed );
1137         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1138         g_object_set_data( holder, "nodes_y_action", eact );
1139         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1140         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1141     }
1143     // add the units menu
1144     {
1145         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1146         gtk_action_group_add_action( mainActions, act );
1147     }
1149     sigc::connection *connection = new sigc::connection (
1150         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1151         );
1153     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1154     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1155 } // end of sp_node_toolbox_prep()
1158 //########################
1159 //##    Zoom Toolbox    ##
1160 //########################
1162 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1164     // no custom GtkAction setup needed
1165 } // end of sp_zoom_toolbox_prep()
1167 void
1168 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1170     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")));
1174 void
1175 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1177     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")));
1180 void
1181 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1183     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")));
1186 static void
1187 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1189     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1190     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1192     if (old_desktop) {
1193         GList *children, *iter;
1195         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1196         for ( iter = children ; iter ; iter = iter->next ) {
1197             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1198         }
1199         g_list_free(children);
1200     }
1202     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1204     if (desktop) {
1205         gtk_widget_set_sensitive(toolbox, TRUE);
1206         setup_func(toolbox, desktop);
1207         update_func(desktop, desktop->event_context, toolbox);
1208         *conn = desktop->connectEventContextChanged
1209             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1210     } else {
1211         gtk_widget_set_sensitive(toolbox, FALSE);
1212     }
1214 } // end of toolbox_set_desktop()
1217 static void
1218 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1220     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1221     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1222     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1223         // "toolbox.tools" was not set. Fallback to older value
1224         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1226         // Copy the setting forwards
1227         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1228     }
1229     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1231     for (int i = 0 ; tools[i].type_name ; i++ ) {
1232         GtkWidget *button =
1233             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1234                                                               SP_BUTTON_TYPE_TOGGLE,
1235                                                               Inkscape::Verb::get(tools[i].verb),
1236                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1237                                                               desktop,
1238                                                               tooltips );
1240         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1241                            (gpointer)button );
1242     }
1246 static void
1247 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1249     gchar const *const tname = ( eventcontext
1250                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1251                                  : NULL );
1252     for (int i = 0 ; tools[i].type_name ; i++ ) {
1253         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1254         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1255     }
1258 static void
1259 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1261     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1262     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1263     GtkUIManager* mgr = gtk_ui_manager_new();
1264     GError* errVal = 0;
1265     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1266     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1268     std::map<std::string, GtkWidget*> dataHolders;
1270     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1271         if ( aux_toolboxes[i].prep_func ) {
1272             // converted to GtkActions and UIManager
1274             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1275             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1276             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1277             dataHolders[aux_toolboxes[i].type_name] = kludge;
1278             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1279         } else {
1281             GtkWidget *sub_toolbox = 0;
1282             if (aux_toolboxes[i].create_func == NULL)
1283                 sub_toolbox = sp_empty_toolbox_new(desktop);
1284             else {
1285                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1286             }
1288             gtk_size_group_add_widget( grouper, sub_toolbox );
1290             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1291             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1293         }
1294     }
1296     // Second pass to create toolbars *after* all GtkActions are created
1297     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1298         if ( aux_toolboxes[i].prep_func ) {
1299             // converted to GtkActions and UIManager
1301             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1303             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1304             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1306             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1307             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1308             g_free( tmp );
1309             tmp = 0;
1311             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1312             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1313             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1314                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1315             }
1316             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1319             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1321             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1322                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1323                 swatch->setDesktop( desktop );
1324                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1325                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1326                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1327                 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 );
1328             }
1330             gtk_widget_show_all( holder );
1331             sp_set_font_size_smaller( holder );
1333             gtk_size_group_add_widget( grouper, holder );
1335             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1336             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1337         }
1338     }
1340     g_object_unref( G_OBJECT(grouper) );
1343 static void
1344 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1346     gchar const *tname = ( eventcontext
1347                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1348                            : NULL );
1349     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1350         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1351         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1352             gtk_widget_show_all(sub_toolbox);
1353             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1354         } else {
1355             gtk_widget_hide(sub_toolbox);
1356         }
1357     }
1360 static void
1361 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1363     gchar const * descr =
1364         "<ui>"
1365         "  <toolbar name='CommandsToolbar'>"
1366         "    <toolitem action='FileNew' />"
1367         "    <toolitem action='FileOpen' />"
1368         "    <toolitem action='FileSave' />"
1369         "    <toolitem action='FilePrint' />"
1370         "    <separator />"
1371         "    <toolitem action='FileImport' />"
1372         "    <toolitem action='FileExport' />"
1373         "    <separator />"
1374         "    <toolitem action='EditUndo' />"
1375         "    <toolitem action='EditRedo' />"
1376         "    <separator />"
1377         "    <toolitem action='EditCopy' />"
1378         "    <toolitem action='EditCut' />"
1379         "    <toolitem action='EditPaste' />"
1380         "    <separator />"
1381         "    <toolitem action='ZoomSelection' />"
1382         "    <toolitem action='ZoomDrawing' />"
1383         "    <toolitem action='ZoomPage' />"
1384         "    <separator />"
1385         "    <toolitem action='EditDuplicate' />"
1386         "    <toolitem action='EditClone' />"
1387         "    <toolitem action='EditUnlinkClone' />"
1388         "    <separator />"
1389         "    <toolitem action='SelectionGroup' />"
1390         "    <toolitem action='SelectionUnGroup' />"
1391         "    <separator />"
1392         "    <toolitem action='DialogFillStroke' />"
1393         "    <toolitem action='DialogText' />"
1394         "    <toolitem action='DialogXMLEditor' />"
1395         "    <toolitem action='DialogAlignDistribute' />"
1396         "    <separator />"
1397         "    <toolitem action='DialogPreferences' />"
1398         "    <toolitem action='DialogDocumentProperties' />"
1399         "  </toolbar>"
1400         "</ui>";
1401     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1404     GtkUIManager* mgr = gtk_ui_manager_new();
1405     GError* errVal = 0;
1407     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1408     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1410     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1411     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1412         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1413     }
1414     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1415     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1416     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1419     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1422 static void
1423 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1427 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1429     gtk_widget_show(toolbox_toplevel);
1430     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1432     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1433     if (!shown_toolbox) {
1434         return;
1435     }
1436     gtk_widget_show(toolbox);
1438     gtk_widget_show_all(shown_toolbox);
1441 void
1442 aux_toolbox_space(GtkWidget *tb, gint space)
1444     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1447 static GtkWidget *
1448 sp_empty_toolbox_new(SPDesktop *desktop)
1450     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1451     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1452     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1454     gtk_widget_show_all(tbl);
1455     sp_set_font_size_smaller (tbl);
1457     return tbl;
1460 // helper UI functions
1462 GtkWidget *
1463 sp_tb_spinbutton(
1464     gchar *label, gchar const *tooltip,
1465     gchar const *path, gchar const *data, gdouble def,
1466     GtkWidget *us,
1467     GtkWidget *tbl,
1468     gboolean altx, gchar const *altx_mark,
1469     gdouble lower, gdouble upper, gdouble step, gdouble page,
1470     void (*callback)(GtkAdjustment *, GtkWidget *),
1471     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1473     GtkTooltips *tt = gtk_tooltips_new();
1475     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1477     GtkWidget *l = gtk_label_new(label);
1478     gtk_widget_show(l);
1479     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1480     gtk_container_add(GTK_CONTAINER(hb), l);
1482     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1483                                       lower, upper, step, page, page);
1484     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1485     if (us)
1486         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1488     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1489     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1490     if (altx)
1491         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1492     gtk_widget_set_size_request(sb,
1493                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1494                                 AUX_SPINBUTTON_HEIGHT);
1495     gtk_widget_show(sb);
1496     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1497     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1498     gtk_container_add(GTK_CONTAINER(hb), sb);
1499     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1501     return hb;
1504 #define MODE_LABEL_WIDTH 70
1506 //########################
1507 //##       Star         ##
1508 //########################
1510 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1512     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1514     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1515         // do not remember prefs if this call is initiated by an undo change, because undoing object
1516         // creation sets bogus values to its attributes before it is deleted
1517         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1518     }
1520     // quit if run by the attr_changed listener
1521     if (g_object_get_data( dataKludge, "freeze" )) {
1522         return;
1523     }
1525     // in turn, prevent listener from responding
1526     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1528     bool modmade = false;
1530     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1531     GSList const *items = selection->itemList();
1532     for (; items != NULL; items = items->next) {
1533         if (SP_IS_STAR((SPItem *) items->data)) {
1534             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1535             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1536             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1537                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1538                                     + M_PI / (gint)adj->value));
1539             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1540             modmade = true;
1541         }
1542     }
1543     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1544                                    _("Star: Change number of corners"));
1546     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1549 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1551     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1553     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1554         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1555     }
1557     // quit if run by the attr_changed listener
1558     if (g_object_get_data( dataKludge, "freeze" )) {
1559         return;
1560     }
1562     // in turn, prevent listener from responding
1563     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1565     bool modmade = false;
1566     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1567     GSList const *items = selection->itemList();
1568     for (; items != NULL; items = items->next) {
1569         if (SP_IS_STAR((SPItem *) items->data)) {
1570             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1572             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1573             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1574             if (r2 < r1) {
1575                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1576             } else {
1577                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1578             }
1580             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1581             modmade = true;
1582         }
1583     }
1585     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1586                                    _("Star: Change spoke ratio"));
1588     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1591 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1593     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1594     bool flat = ege_select_one_action_get_active( act ) == 0;
1596     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1597         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1598                                     flat ? "true" : "false" );
1599     }
1601     // quit if run by the attr_changed listener
1602     if (g_object_get_data( dataKludge, "freeze" )) {
1603         return;
1604     }
1606     // in turn, prevent listener from responding
1607     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1609     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1610     GSList const *items = selection->itemList();
1611     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1612     bool modmade = false;
1614     if ( prop_action ) {
1615         gtk_action_set_sensitive( prop_action, !flat );
1616     }
1618     for (; items != NULL; items = items->next) {
1619         if (SP_IS_STAR((SPItem *) items->data)) {
1620             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1621             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1622             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1623             modmade = true;
1624         }
1625     }
1627     if (modmade) {
1628         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1629                          flat ? _("Make polygon") : _("Make star"));
1630     }
1632     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1635 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1637     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1639     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1640         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1641     }
1643     // quit if run by the attr_changed listener
1644     if (g_object_get_data( dataKludge, "freeze" )) {
1645         return;
1646     }
1648     // in turn, prevent listener from responding
1649     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1651     bool modmade = false;
1653     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1654     GSList const *items = selection->itemList();
1655     for (; items != NULL; items = items->next) {
1656         if (SP_IS_STAR((SPItem *) items->data)) {
1657             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1658             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1659             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1660             modmade = true;
1661         }
1662     }
1663     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1664                                    _("Star: Change rounding"));
1666     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1669 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1671     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1673     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1674         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1675     }
1677     // quit if run by the attr_changed listener
1678     if (g_object_get_data( dataKludge, "freeze" )) {
1679         return;
1680     }
1682     // in turn, prevent listener from responding
1683     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1685     bool modmade = false;
1687     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1688     GSList const *items = selection->itemList();
1689     for (; items != NULL; items = items->next) {
1690         if (SP_IS_STAR((SPItem *) items->data)) {
1691             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1692             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1693             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1694             modmade = true;
1695         }
1696     }
1697     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1698                                    _("Star: Change randomization"));
1700     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1704 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1705                                        gchar const */*old_value*/, gchar const */*new_value*/,
1706                                        bool /*is_interactive*/, gpointer data)
1708     GtkWidget *tbl = GTK_WIDGET(data);
1710     // quit if run by the _changed callbacks
1711     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1712         return;
1713     }
1715     // in turn, prevent callbacks from responding
1716     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1718     GtkAdjustment *adj = 0;
1720     if (!strcmp(name, "inkscape:randomized")) {
1721         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1722         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1723     } else if (!strcmp(name, "inkscape:rounded")) {
1724         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1725         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1726     } else if (!strcmp(name, "inkscape:flatsided")) {
1727         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1728         char const *flatsides = repr->attribute("inkscape:flatsided");
1729         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1730         if ( flatsides && !strcmp(flatsides,"false") ) {
1731             ege_select_one_action_set_active( flat_action, 1 );
1732             gtk_action_set_sensitive( prop_action, TRUE );
1733         } else {
1734             ege_select_one_action_set_active( flat_action, 0 );
1735             gtk_action_set_sensitive( prop_action, FALSE );
1736         }
1737     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1738         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1739         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1740         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1741         if (r2 < r1) {
1742             gtk_adjustment_set_value(adj, r2/r1);
1743         } else {
1744             gtk_adjustment_set_value(adj, r1/r2);
1745         }
1746     } else if (!strcmp(name, "sodipodi:sides")) {
1747         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1748         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1749     }
1751     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1755 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1757     NULL, /* child_added */
1758     NULL, /* child_removed */
1759     star_tb_event_attr_changed,
1760     NULL, /* content_changed */
1761     NULL  /* order_changed */
1762 };
1765 /**
1766  *  \param selection Should not be NULL.
1767  */
1768 static void
1769 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1771     int n_selected = 0;
1772     Inkscape::XML::Node *repr = NULL;
1774     purge_repr_listener( tbl, tbl );
1776     for (GSList const *items = selection->itemList();
1777          items != NULL;
1778          items = items->next)
1779     {
1780         if (SP_IS_STAR((SPItem *) items->data)) {
1781             n_selected++;
1782             repr = SP_OBJECT_REPR((SPItem *) items->data);
1783         }
1784     }
1786     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1788     if (n_selected == 0) {
1789         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1790     } else if (n_selected == 1) {
1791         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1793         if (repr) {
1794             g_object_set_data( tbl, "repr", repr );
1795             Inkscape::GC::anchor(repr);
1796             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1797             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1798         }
1799     } else {
1800         // FIXME: implement averaging of all parameters for multiple selected stars
1801         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1802         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1803     }
1807 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1809     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1810     // callbacks to lump all the changes for all selected objects in one undo step
1812     GtkAdjustment *adj = 0;
1814     // fixme: make settable in prefs!
1815     gint mag = 5;
1816     gdouble prop = 0.5;
1817     gboolean flat = FALSE;
1818     gdouble randomized = 0;
1819     gdouble rounded = 0;
1821     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1822     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1824     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1825     gtk_action_set_sensitive( sb2, !flat );
1827     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1828     gtk_adjustment_set_value(adj, mag);
1829     gtk_adjustment_value_changed(adj);
1831     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1832     gtk_adjustment_set_value(adj, prop);
1833     gtk_adjustment_value_changed(adj);
1835     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1836     gtk_adjustment_set_value(adj, rounded);
1837     gtk_adjustment_value_changed(adj);
1839     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1840     gtk_adjustment_set_value(adj, randomized);
1841     gtk_adjustment_value_changed(adj);
1845 void
1846 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1848     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1849     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1850     GtkWidget *l = gtk_label_new(NULL);
1851     gtk_label_set_markup(GTK_LABEL(l), title);
1852     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1853     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1854     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1858 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1860     {
1861         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1862         ege_output_action_set_use_markup( act, TRUE );
1863         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1864         g_object_set_data( holder, "mode_action", act );
1865     }
1867     {
1868         EgeAdjustmentAction* eact = 0;
1869         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1870         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1872         /* Flatsided checkbox */
1873         {
1874             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1876             GtkTreeIter iter;
1877             gtk_list_store_append( model, &iter );
1878             gtk_list_store_set( model, &iter,
1879                                 0, _("Polygon"),
1880                                 1, _("Regular polygon (with one handle) instead of a star"),
1881                                 2, "star_flat",
1882                                 -1 );
1884             gtk_list_store_append( model, &iter );
1885             gtk_list_store_set( model, &iter,
1886                                 0, _("Star"),
1887                                 1, _("Star instead of a regular polygon (with one handle)"),
1888                                 2, "star_angled",
1889                                 -1 );
1891             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1892             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1893             g_object_set_data( holder, "flat_action", act );
1895             ege_select_one_action_set_appearance( act, "full" );
1896             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1897             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1898             ege_select_one_action_set_icon_column( act, 2 );
1899             ege_select_one_action_set_tooltip_column( act, 1  );
1901             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1902             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1903         }
1905         /* Magnitude */
1906         {
1907         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1908         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1909         eact = create_adjustment_action( "MagnitudeAction",
1910                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1911                                          "tools.shapes.star", "magnitude", 3,
1912                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1913                                          3, 1024, 1, 5,
1914                                          labels, values, G_N_ELEMENTS(labels),
1915                                          sp_stb_magnitude_value_changed,
1916                                          1.0, 0 );
1917         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1918         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1919         }
1921         /* Spoke ratio */
1922         {
1923         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1924         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1925         eact = create_adjustment_action( "SpokeAction",
1926                                          _("Spoke ratio"), _("Spoke ratio:"),
1927                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1928                                          // Base radius is the same for the closest handle.
1929                                          _("Base radius to tip radius ratio"),
1930                                          "tools.shapes.star", "proportion", 0.5,
1931                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1932                                          0.01, 1.0, 0.01, 0.1,
1933                                          labels, values, G_N_ELEMENTS(labels),
1934                                          sp_stb_proportion_value_changed );
1935         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1936         g_object_set_data( holder, "prop_action", eact );
1937         }
1939         if ( !isFlatSided ) {
1940             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1941         } else {
1942             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1943         }
1945         /* Roundedness */
1946         {
1947         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1948         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1949         eact = create_adjustment_action( "RoundednessAction",
1950                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1951                                          "tools.shapes.star", "rounded", 0.0,
1952                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1953                                          -10.0, 10.0, 0.01, 0.1,
1954                                          labels, values, G_N_ELEMENTS(labels),
1955                                          sp_stb_rounded_value_changed );
1956         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1957         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1958         }
1960         /* Randomization */
1961         {
1962         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1963         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1964         eact = create_adjustment_action( "RandomizationAction",
1965                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1966                                          "tools.shapes.star", "randomized", 0.0,
1967                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1968                                          -10.0, 10.0, 0.001, 0.01,
1969                                          labels, values, G_N_ELEMENTS(labels),
1970                                          sp_stb_randomized_value_changed, 0.1, 3 );
1971         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1972         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1973         }
1974     }
1976     {
1977         /* Reset */
1978         {
1979             GtkAction* act = gtk_action_new( "StarResetAction",
1980                                              _("Defaults"),
1981                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1982                                              GTK_STOCK_CLEAR );
1983             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1984             gtk_action_group_add_action( mainActions, act );
1985             gtk_action_set_sensitive( act, TRUE );
1986         }
1987     }
1989     sigc::connection *connection = new sigc::connection(
1990         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1991         );
1992     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1993     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1997 //########################
1998 //##       Rect         ##
1999 //########################
2001 static void sp_rtb_sensitivize( GObject *tbl )
2003     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2004     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2005     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2007     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2008         gtk_action_set_sensitive( not_rounded, FALSE );
2009     } else {
2010         gtk_action_set_sensitive( not_rounded, TRUE );
2011     }
2015 static void
2016 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2017                           void (*setter)(SPRect *, gdouble))
2019     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2021     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2022     SPUnit const *unit = tracker->getActiveUnit();
2024     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2025         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2026     }
2028     // quit if run by the attr_changed listener
2029     if (g_object_get_data( tbl, "freeze" )) {
2030         return;
2031     }
2033     // in turn, prevent listener from responding
2034     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2036     bool modmade = false;
2037     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2038     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2039         if (SP_IS_RECT(items->data)) {
2040             if (adj->value != 0) {
2041                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2042             } else {
2043                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2044             }
2045             modmade = true;
2046         }
2047     }
2049     sp_rtb_sensitivize( tbl );
2051     if (modmade) {
2052         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2053                                    _("Change rectangle"));
2054     }
2056     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2059 static void
2060 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2062     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2065 static void
2066 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2068     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2071 static void
2072 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2074     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2077 static void
2078 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2080     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2085 static void
2086 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2088     GtkAdjustment *adj = 0;
2090     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2091     gtk_adjustment_set_value(adj, 0.0);
2092     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2093     gtk_adjustment_value_changed(adj);
2095     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2096     gtk_adjustment_set_value(adj, 0.0);
2097     gtk_adjustment_value_changed(adj);
2099     sp_rtb_sensitivize( obj );
2102 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2103                                        gchar const */*old_value*/, gchar const */*new_value*/,
2104                                        bool /*is_interactive*/, gpointer data)
2106     GObject *tbl = G_OBJECT(data);
2108     // quit if run by the _changed callbacks
2109     if (g_object_get_data( tbl, "freeze" )) {
2110         return;
2111     }
2113     // in turn, prevent callbacks from responding
2114     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2116     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2117     SPUnit const *unit = tracker->getActiveUnit();
2119     gpointer item = g_object_get_data( tbl, "item" );
2120     if (item && SP_IS_RECT(item)) {
2121         {
2122             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2123             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2124             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2125         }
2127         {
2128             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2129             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2130             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2131         }
2133         {
2134             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2135             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2136             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2137         }
2139         {
2140             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2141             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2142             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2143         }
2144     }
2146     sp_rtb_sensitivize( tbl );
2148     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2152 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2153     NULL, /* child_added */
2154     NULL, /* child_removed */
2155     rect_tb_event_attr_changed,
2156     NULL, /* content_changed */
2157     NULL  /* order_changed */
2158 };
2160 /**
2161  *  \param selection should not be NULL.
2162  */
2163 static void
2164 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2166     int n_selected = 0;
2167     Inkscape::XML::Node *repr = NULL;
2168     SPItem *item = NULL;
2170     if ( g_object_get_data( tbl, "repr" ) ) {
2171         g_object_set_data( tbl, "item", NULL );
2172     }
2173     purge_repr_listener( tbl, tbl );
2175     for (GSList const *items = selection->itemList();
2176          items != NULL;
2177          items = items->next) {
2178         if (SP_IS_RECT((SPItem *) items->data)) {
2179             n_selected++;
2180             item = (SPItem *) items->data;
2181             repr = SP_OBJECT_REPR(item);
2182         }
2183     }
2185     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2187     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2189     if (n_selected == 0) {
2190         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2192         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2193         gtk_action_set_sensitive(w, FALSE);
2194         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2195         gtk_action_set_sensitive(h, FALSE);
2197     } else if (n_selected == 1) {
2198         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2199         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2201         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2202         gtk_action_set_sensitive(w, TRUE);
2203         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2204         gtk_action_set_sensitive(h, TRUE);
2206         if (repr) {
2207             g_object_set_data( tbl, "repr", repr );
2208             g_object_set_data( tbl, "item", item );
2209             Inkscape::GC::anchor(repr);
2210             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2211             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2212         }
2213     } else {
2214         // FIXME: implement averaging of all parameters for multiple selected
2215         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2216         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2217         sp_rtb_sensitivize( tbl );
2218     }
2222 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2224     EgeAdjustmentAction* eact = 0;
2226     {
2227         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2228         ege_output_action_set_use_markup( act, TRUE );
2229         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2230         g_object_set_data( holder, "mode_action", act );
2231     }
2233     // rx/ry units menu: create
2234     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2235     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2236     // fixme: add % meaning per cent of the width/height
2237     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2238     g_object_set_data( holder, "tracker", tracker );
2240     /* W */
2241     {
2242         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2243         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2244         eact = create_adjustment_action( "RectWidthAction",
2245                                          _("Width"), _("W:"), _("Width of rectangle"),
2246                                          "tools.shapes.rect", "width", 0,
2247                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2248                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2249                                          labels, values, G_N_ELEMENTS(labels),
2250                                          sp_rtb_width_value_changed );
2251         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2252         g_object_set_data( holder, "width_action", eact );
2253         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2254         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2255     }
2257     /* H */
2258     {
2259         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2260         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2261         eact = create_adjustment_action( "RectHeightAction",
2262                                          _("Height"), _("H:"), _("Height of rectangle"),
2263                                          "tools.shapes.rect", "height", 0,
2264                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2265                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2266                                          labels, values, G_N_ELEMENTS(labels),
2267                                          sp_rtb_height_value_changed );
2268         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2269         g_object_set_data( holder, "height_action", eact );
2270         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2271         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2272     }
2274     /* rx */
2275     {
2276         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2277         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2278         eact = create_adjustment_action( "RadiusXAction",
2279                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2280                                          "tools.shapes.rect", "rx", 0,
2281                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2282                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2283                                          labels, values, G_N_ELEMENTS(labels),
2284                                          sp_rtb_rx_value_changed);
2285         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2286         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2287     }
2289     /* ry */
2290     {
2291         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2292         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2293         eact = create_adjustment_action( "RadiusYAction",
2294                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2295                                          "tools.shapes.rect", "ry", 0,
2296                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2297                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2298                                          labels, values, G_N_ELEMENTS(labels),
2299                                          sp_rtb_ry_value_changed);
2300         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2301         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2302     }
2304     // add the units menu
2305     {
2306         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2307         gtk_action_group_add_action( mainActions, act );
2308     }
2310     /* Reset */
2311     {
2312         InkAction* inky = ink_action_new( "RectResetAction",
2313                                           _("Not rounded"),
2314                                           _("Make corners sharp"),
2315                                           "squared_corner",
2316                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2317         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2318         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2319         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2320         g_object_set_data( holder, "not_rounded", inky );
2321     }
2323     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2324     sp_rtb_sensitivize( holder );
2326     sigc::connection *connection = new sigc::connection(
2327         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2328         );
2329     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2330     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2333 //########################
2334 //##       3D Box       ##
2335 //########################
2337 // normalize angle so that it lies in the interval [0,360]
2338 static double box3d_normalize_angle (double a) {
2339     double angle = a + ((int) (a/360.0))*360;
2340     if (angle < 0) {
2341         angle += 360.0;
2342     }
2343     return angle;
2346 static void
2347 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2348                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2349     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2350     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2351     //       are reset).
2352     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2354     if (is_infinite) {
2355         gtk_toggle_action_set_active(tact, TRUE);
2356         gtk_action_set_sensitive(act, TRUE);
2358         double angle = persp3d_get_infinite_angle(persp, axis);
2359         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2360             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2361         }
2362     } else {
2363         gtk_toggle_action_set_active(tact, FALSE);
2364         gtk_action_set_sensitive(act, FALSE);
2365     }
2368 static void
2369 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2370     if (!persp_repr) {
2371         g_print ("No perspective given to box3d_resync_toolbar().\n");
2372         return;
2373     }
2375     GtkWidget *tbl = GTK_WIDGET(data);
2376     GtkAdjustment *adj = 0;
2377     GtkAction *act = 0;
2378     GtkToggleAction *tact = 0;
2379     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2380     {
2381         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2382         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2383         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2385         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2386     }
2387     {
2388         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2389         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2390         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2392         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2393     }
2394     {
2395         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2396         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2397         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2399         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2400     }
2403 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2404                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2405                                                   bool /*is_interactive*/, gpointer data)
2407     GtkWidget *tbl = GTK_WIDGET(data);
2409     // quit if run by the attr_changed listener
2410     // note: it used to work without the differently called freeze_ attributes (here and in
2411     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2412     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2413         return;
2414     }
2416     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2417     // sp_document_maybe_done() when the document is undo insensitive)
2418     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2420     // TODO: Only update the appropriate part of the toolbar
2421 //    if (!strcmp(name, "inkscape:vp_z")) {
2422         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2423 //    }
2425     Persp3D *persp = persp3d_get_from_repr(repr);
2426     persp3d_update_box_reprs(persp);
2428     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2431 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2433     NULL, /* child_added */
2434     NULL, /* child_removed */
2435     box3d_persp_tb_event_attr_changed,
2436     NULL, /* content_changed */
2437     NULL  /* order_changed */
2438 };
2440 /**
2441  *  \param selection Should not be NULL.
2442  */
2443 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2444 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2445 static void
2446 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2448     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2449     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2450     // update the perspectives with infinite VPs and leave the other ones untouched).
2452     Inkscape::XML::Node *persp_repr = NULL;
2453     purge_repr_listener(tbl, tbl);
2455     SPItem *item = selection->singleItem();
2456     if (item && SP_IS_BOX3D(item)) {
2457         // FIXME: Also deal with multiple selected boxes
2458         SPBox3D *box = SP_BOX3D(item);
2459         Persp3D *persp = box3d_get_perspective(box);
2460         persp_repr = SP_OBJECT_REPR(persp);
2461         if (persp_repr) {
2462             g_object_set_data(tbl, "repr", persp_repr);
2463             Inkscape::GC::anchor(persp_repr);
2464             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2465             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2466         }
2468         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2469         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2470     
2471         box3d_resync_toolbar(persp_repr, tbl);
2472     }
2475 static void
2476 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2478     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2479     SPDocument *document = sp_desktop_document(desktop);
2481     // quit if run by the attr_changed listener
2482     // note: it used to work without the differently called freeze_ attributes (here and in
2483     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2484     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2485         return;
2486     }
2488     // in turn, prevent listener from responding
2489     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2491     //Persp3D *persp = document->current_persp3d;
2492     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2493     if (sel_persps.empty()) {
2494         // this can happen when the document is created; we silently ignore it
2495         return;
2496     }
2497     Persp3D *persp = *(sel_persps.begin());
2499     persp->tmat.set_infinite_direction (axis, adj->value);
2500     SP_OBJECT(persp)->updateRepr();
2502     // TODO: use the correct axis here, too
2503     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2505     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2509 static void
2510 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2512     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2515 static void
2516 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2518     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2521 static void
2522 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2524     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2528 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2530     // TODO: Take all selected perspectives into account
2531     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2532     if (sel_persps.empty()) {
2533         // this can happen when the document is created; we silently ignore it
2534         return;
2535     }
2536     Persp3D *persp = *(sel_persps.begin());
2538     bool set_infinite = gtk_toggle_action_get_active(act);
2539     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2542 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2544     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2547 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2549     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2552 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2554     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2557 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2559     EgeAdjustmentAction* eact = 0;
2560     SPDocument *document = sp_desktop_document (desktop);
2561     Persp3D *persp = document->current_persp3d;
2563     EgeAdjustmentAction* box3d_angle_x = 0;
2564     EgeAdjustmentAction* box3d_angle_y = 0;
2565     EgeAdjustmentAction* box3d_angle_z = 0;
2567     /* Angle X */
2568     {
2569         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2570         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2571         eact = create_adjustment_action( "3DBoxAngleXAction",
2572                                          _("Angle in X direction"), _("Angle X:"),
2573                                          // Translators: PL is short for 'perspective line'
2574                                          _("Angle of PLs in X direction"),
2575                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2576                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2577                                          -360.0, 360.0, 1.0, 10.0,
2578                                          labels, values, G_N_ELEMENTS(labels),
2579                                          box3d_angle_x_value_changed );
2580         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2581         g_object_set_data( holder, "box3d_angle_x_action", eact );
2582         box3d_angle_x = eact;
2583     }
2585     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2586         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2587     } else {
2588         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2589     }
2592     /* VP X state */
2593     {
2594         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2595                                                       // Translators: VP is short for 'vanishing point'
2596                                                       _("State of VP in X direction"),
2597                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2598                                                       "toggle_vp_x",
2599                                                       Inkscape::ICON_SIZE_DECORATION );
2600         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2601         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2602         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2603         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2604         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2605     }
2607     /* Angle Y */
2608     {
2609         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2610         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2611         eact = create_adjustment_action( "3DBoxAngleYAction",
2612                                          _("Angle in Y direction"), _("Angle Y:"),
2613                                          // Translators: PL is short for 'perspective line'
2614                                          _("Angle of PLs in Y direction"),
2615                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2616                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2617                                          -360.0, 360.0, 1.0, 10.0,
2618                                          labels, values, G_N_ELEMENTS(labels),
2619                                          box3d_angle_y_value_changed );
2620         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2621         g_object_set_data( holder, "box3d_angle_y_action", eact );
2622         box3d_angle_y = eact;
2623     }
2625     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2626         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2627     } else {
2628         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2629     }
2631     /* VP Y state */
2632     {
2633         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2634                                                       // Translators: VP is short for 'vanishing point'
2635                                                       _("State of VP in Y direction"),
2636                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2637                                                       "toggle_vp_y",
2638                                                       Inkscape::ICON_SIZE_DECORATION );
2639         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2640         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2641         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2642         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2643         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2644     }
2646     /* Angle Z */
2647     {
2648         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2649         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2650         eact = create_adjustment_action( "3DBoxAngleZAction",
2651                                          _("Angle in Z direction"), _("Angle Z:"),
2652                                          // Translators: PL is short for 'perspective line'
2653                                          _("Angle of PLs in Z direction"),
2654                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2655                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2656                                          -360.0, 360.0, 1.0, 10.0,
2657                                          labels, values, G_N_ELEMENTS(labels),
2658                                          box3d_angle_z_value_changed );
2659         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2660         g_object_set_data( holder, "box3d_angle_z_action", eact );
2661         box3d_angle_z = eact;
2662     }
2664     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2665         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2666     } else {
2667         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2668     }
2670     /* VP Z state */
2671     {
2672         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2673                                                       // Translators: VP is short for 'vanishing point'
2674                                                       _("State of VP in Z direction"),
2675                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2676                                                       "toggle_vp_z",
2677                                                       Inkscape::ICON_SIZE_DECORATION );
2678         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2679         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2680         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2681         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2682         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2683     }
2685     sigc::connection *connection = new sigc::connection(
2686         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2687        );
2688     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2689     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2692 //########################
2693 //##       Spiral       ##
2694 //########################
2696 static void
2697 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2699     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2701     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2702         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2703     }
2705     // quit if run by the attr_changed listener
2706     if (g_object_get_data( tbl, "freeze" )) {
2707         return;
2708     }
2710     // in turn, prevent listener from responding
2711     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2713     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2715     bool modmade = false;
2716     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2717          items != NULL;
2718          items = items->next)
2719     {
2720         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2721             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2722             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2723             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2724             modmade = true;
2725         }
2726     }
2728     g_free(namespaced_name);
2730     if (modmade) {
2731         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2732                                    _("Change spiral"));
2733     }
2735     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2738 static void
2739 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2741     sp_spl_tb_value_changed(adj, tbl, "revolution");
2744 static void
2745 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2747     sp_spl_tb_value_changed(adj, tbl, "expansion");
2750 static void
2751 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2753     sp_spl_tb_value_changed(adj, tbl, "t0");
2756 static void
2757 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2759     GtkWidget *tbl = GTK_WIDGET(obj);
2761     GtkAdjustment *adj;
2763     // fixme: make settable
2764     gdouble rev = 5;
2765     gdouble exp = 1.0;
2766     gdouble t0 = 0.0;
2768     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2769     gtk_adjustment_set_value(adj, rev);
2770     gtk_adjustment_value_changed(adj);
2772     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2773     gtk_adjustment_set_value(adj, exp);
2774     gtk_adjustment_value_changed(adj);
2776     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2777     gtk_adjustment_set_value(adj, t0);
2778     gtk_adjustment_value_changed(adj);
2780     spinbutton_defocus(GTK_OBJECT(tbl));
2784 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2785                                          gchar const */*old_value*/, gchar const */*new_value*/,
2786                                          bool /*is_interactive*/, gpointer data)
2788     GtkWidget *tbl = GTK_WIDGET(data);
2790     // quit if run by the _changed callbacks
2791     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2792         return;
2793     }
2795     // in turn, prevent callbacks from responding
2796     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2798     GtkAdjustment *adj;
2799     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2800     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2802     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2803     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2805     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2806     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2808     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2812 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2813     NULL, /* child_added */
2814     NULL, /* child_removed */
2815     spiral_tb_event_attr_changed,
2816     NULL, /* content_changed */
2817     NULL  /* order_changed */
2818 };
2820 static void
2821 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2823     int n_selected = 0;
2824     Inkscape::XML::Node *repr = NULL;
2826     purge_repr_listener( tbl, tbl );
2828     for (GSList const *items = selection->itemList();
2829          items != NULL;
2830          items = items->next)
2831     {
2832         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2833             n_selected++;
2834             repr = SP_OBJECT_REPR((SPItem *) items->data);
2835         }
2836     }
2838     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2840     if (n_selected == 0) {
2841         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2842     } else if (n_selected == 1) {
2843         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2845         if (repr) {
2846             g_object_set_data( tbl, "repr", repr );
2847             Inkscape::GC::anchor(repr);
2848             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2849             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2850         }
2851     } else {
2852         // FIXME: implement averaging of all parameters for multiple selected
2853         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2854         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2855     }
2859 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2861     EgeAdjustmentAction* eact = 0;
2863     {
2864         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2865         ege_output_action_set_use_markup( act, TRUE );
2866         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2867         g_object_set_data( holder, "mode_action", act );
2868     }
2870     /* Revolution */
2871     {
2872         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2873         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2874         eact = create_adjustment_action( "SpiralRevolutionAction",
2875                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2876                                          "tools.shapes.spiral", "revolution", 3.0,
2877                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2878                                          0.01, 1024.0, 0.1, 1.0,
2879                                          labels, values, G_N_ELEMENTS(labels),
2880                                          sp_spl_tb_revolution_value_changed, 1, 2);
2881         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2882     }
2884     /* Expansion */
2885     {
2886         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2887         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2888         eact = create_adjustment_action( "SpiralExpansionAction",
2889                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2890                                          "tools.shapes.spiral", "expansion", 1.0,
2891                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2892                                          0.0, 1000.0, 0.01, 1.0,
2893                                          labels, values, G_N_ELEMENTS(labels),
2894                                          sp_spl_tb_expansion_value_changed);
2895         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2896     }
2898     /* T0 */
2899     {
2900         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2901         gdouble values[] = {0, 0.5, 0.9};
2902         eact = create_adjustment_action( "SpiralT0Action",
2903                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2904                                          "tools.shapes.spiral", "t0", 0.0,
2905                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2906                                          0.0, 0.999, 0.01, 1.0,
2907                                          labels, values, G_N_ELEMENTS(labels),
2908                                          sp_spl_tb_t0_value_changed);
2909         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2910     }
2912     /* Reset */
2913     {
2914         InkAction* inky = ink_action_new( "SpiralResetAction",
2915                                           _("Defaults"),
2916                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2917                                           GTK_STOCK_CLEAR,
2918                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2919         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2920         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2921     }
2924     sigc::connection *connection = new sigc::connection(
2925         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2926         );
2927     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2928     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2931 //########################
2932 //##     Pen/Pencil    ##
2933 //########################
2936 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2938     // Put stuff here
2941 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2943     // Put stuff here
2946 //########################
2947 //##       Tweak        ##
2948 //########################
2950 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2952     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2955 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2957     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2960 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2962     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2965 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2967     int mode = ege_select_one_action_get_active( act );
2968     prefs_set_int_attribute("tools.tweak", "mode", mode);
2970     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2971     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2972     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2973     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2974     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2975     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2976     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2977         if (doh) gtk_action_set_sensitive (doh, TRUE);
2978         if (dos) gtk_action_set_sensitive (dos, TRUE);
2979         if (dol) gtk_action_set_sensitive (dol, TRUE);
2980         if (doo) gtk_action_set_sensitive (doo, TRUE);
2981         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2982         if (fid) gtk_action_set_sensitive (fid, FALSE);
2983     } else {
2984         if (doh) gtk_action_set_sensitive (doh, FALSE);
2985         if (dos) gtk_action_set_sensitive (dos, FALSE);
2986         if (dol) gtk_action_set_sensitive (dol, FALSE);
2987         if (doo) gtk_action_set_sensitive (doo, FALSE);
2988         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2989         if (fid) gtk_action_set_sensitive (fid, TRUE);
2990     }
2993 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2995     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2998 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2999     bool show = gtk_toggle_action_get_active( act );
3000     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3002 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3003     bool show = gtk_toggle_action_get_active( act );
3004     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3006 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3007     bool show = gtk_toggle_action_get_active( act );
3008     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3010 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3011     bool show = gtk_toggle_action_get_active( act );
3012     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3015 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3017     {
3018         /* Width */
3019         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3020         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3021         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3022                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3023                                                               "tools.tweak", "width", 15,
3024                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3025                                                               1, 100, 1.0, 10.0,
3026                                                               labels, values, G_N_ELEMENTS(labels),
3027                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3028         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3029         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3030     }
3033     {
3034         /* Force */
3035         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3036         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3037         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3038                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3039                                                               "tools.tweak", "force", 20,
3040                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3041                                                               1, 100, 1.0, 10.0,
3042                                                               labels, values, G_N_ELEMENTS(labels),
3043                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3044         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3045         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3046     }
3048     /* Mode */
3049     {
3050         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3052         GtkTreeIter iter;
3053         gtk_list_store_append( model, &iter );
3054         gtk_list_store_set( model, &iter,
3055                             0, _("Push mode"),
3056                             1, _("Push parts of paths in any direction"),
3057                             2, "tweak_push_mode",
3058                             -1 );
3060         gtk_list_store_append( model, &iter );
3061         gtk_list_store_set( model, &iter,
3062                             0, _("Shrink mode"),
3063                             1, _("Shrink (inset) parts of paths"),
3064                             2, "tweak_shrink_mode",
3065                             -1 );
3067         gtk_list_store_append( model, &iter );
3068         gtk_list_store_set( model, &iter,
3069                             0, _("Grow mode"),
3070                             1, _("Grow (outset) parts of paths"),
3071                             2, "tweak_grow_mode",
3072                             -1 );
3074         gtk_list_store_append( model, &iter );
3075         gtk_list_store_set( model, &iter,
3076                             0, _("Attract mode"),
3077                             1, _("Attract parts of paths towards cursor"),
3078                             2, "tweak_attract_mode",
3079                             -1 );
3081         gtk_list_store_append( model, &iter );
3082         gtk_list_store_set( model, &iter,
3083                             0, _("Repel mode"),
3084                             1, _("Repel parts of paths from cursor"),
3085                             2, "tweak_repel_mode",
3086                             -1 );
3088         gtk_list_store_append( model, &iter );
3089         gtk_list_store_set( model, &iter,
3090                             0, _("Roughen mode"),
3091                             1, _("Roughen parts of paths"),
3092                             2, "tweak_roughen_mode",
3093                             -1 );
3095         gtk_list_store_append( model, &iter );
3096         gtk_list_store_set( model, &iter,
3097                             0, _("Color paint mode"),
3098                             1, _("Paint the tool's color upon selected objects"),
3099                             2, "tweak_colorpaint_mode",
3100                             -1 );
3102         gtk_list_store_append( model, &iter );
3103         gtk_list_store_set( model, &iter,
3104                             0, _("Color jitter mode"),
3105                             1, _("Jitter the colors of selected objects"),
3106                             2, "tweak_colorjitter_mode",
3107                             -1 );
3109         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3110         g_object_set( act, "short_label", _("Mode:"), NULL );
3111         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3112         g_object_set_data( holder, "mode_action", act );
3114         ege_select_one_action_set_appearance( act, "full" );
3115         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3116         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3117         ege_select_one_action_set_icon_column( act, 2 );
3118         ege_select_one_action_set_tooltip_column( act, 1  );
3120         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3121         ege_select_one_action_set_active( act, mode );
3122         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3124         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3125     }
3127     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3129     {
3130         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3131         ege_output_action_set_use_markup( act, TRUE );
3132         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3133         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3134             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3135         g_object_set_data( holder, "tweak_channels_label", act);
3136     }
3138     {
3139         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3140                                                       _("Hue"),
3141                                                       _("In color mode, act on objects' hue"),
3142                                                       NULL,
3143                                                       Inkscape::ICON_SIZE_DECORATION );
3144         g_object_set( act, "short_label", _("H"), NULL );
3145         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3146         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3147         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3148         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3149             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3150         g_object_set_data( holder, "tweak_doh", act);
3151     }
3152     {
3153         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3154                                                       _("Saturation"),
3155                                                       _("In color mode, act on objects' saturation"),
3156                                                       NULL,
3157                                                       Inkscape::ICON_SIZE_DECORATION );
3158         g_object_set( act, "short_label", _("S"), NULL );
3159         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3160         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3161         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3162         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3163             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3164         g_object_set_data( holder, "tweak_dos", act );
3165     }
3166     {
3167         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3168                                                       _("Lightness"),
3169                                                       _("In color mode, act on objects' lightness"),
3170                                                       NULL,
3171                                                       Inkscape::ICON_SIZE_DECORATION );
3172         g_object_set( act, "short_label", _("L"), NULL );
3173         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3174         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3175         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3176         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3177             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3178         g_object_set_data( holder, "tweak_dol", act );
3179     }
3180     {
3181         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3182                                                       _("Opacity"),
3183                                                       _("In color mode, act on objects' opacity"),
3184                                                       NULL,
3185                                                       Inkscape::ICON_SIZE_DECORATION );
3186         g_object_set( act, "short_label", _("O"), NULL );
3187         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3188         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3189         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3190         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3191             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3192         g_object_set_data( holder, "tweak_doo", act );
3193     }
3195     {   /* Fidelity */
3196         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3197         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3198         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3199                                                               _("Fidelity"), _("Fidelity:"),
3200                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3201                                                               "tools.tweak", "fidelity", 50,
3202                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3203                                                               1, 100, 1.0, 10.0,
3204                                                               labels, values, G_N_ELEMENTS(labels),
3205                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3206         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3207         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3208         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3209             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3210         g_object_set_data( holder, "tweak_fidelity", eact );
3211     }
3214     /* Use Pressure button */
3215     {
3216         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3217                                                       _("Pressure"),
3218                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3219                                                       "use_pressure",
3220                                                       Inkscape::ICON_SIZE_DECORATION );
3221         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3222         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3223         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3224     }
3229 //########################
3230 //##     Calligraphy    ##
3231 //########################
3233 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3235     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3238 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3240     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3243 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3245     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3248 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3250     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3253 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3255     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3258 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3260     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3263 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3265     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3268 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3270     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3273 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3275     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3278 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3280     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3283 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3285     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3287     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3290 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3292     // FIXME: make defaults settable via Inkscape Options
3293     struct KeyValue {
3294         char const *key;
3295         double value;
3296     } const key_values[] = {
3297         {"mass", 0.02},
3298         {"wiggle", 0.0},
3299         {"angle", 30.0},
3300         {"width", 15},
3301         {"thinning", 0.1},
3302         {"tremor", 0.0},
3303         {"flatness", 0.9},
3304         {"cap_rounding", 0.0}
3305     };
3307     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3308         KeyValue const &kv = key_values[i];
3309         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3310         if ( adj ) {
3311             gtk_adjustment_set_value(adj, kv.value);
3312         }
3313     }
3317 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3319     {
3320         EgeAdjustmentAction* calligraphy_angle = 0;
3322         {
3323         /* Width */
3324         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3325         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3326         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3327                                                               _("Pen Width"), _("Width:"),
3328                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3329                                                               "tools.calligraphic", "width", 15,
3330                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3331                                                               1, 100, 1.0, 10.0,
3332                                                               labels, values, G_N_ELEMENTS(labels),
3333                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3334         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3335         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3336         }
3338         {
3339         /* Thinning */
3340             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3341             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3342         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3343                                                               _("Stroke Thinning"), _("Thinning:"),
3344                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3345                                                               "tools.calligraphic", "thinning", 0.1,
3346                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3347                                                               -1.0, 1.0, 0.01, 0.1,
3348                                                               labels, values, G_N_ELEMENTS(labels),
3349                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3350         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3351         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3352         }
3354         {
3355         /* Angle */
3356         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3357         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3358         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3359                                                               _("Pen Angle"), _("Angle:"),
3360                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3361                                                               "tools.calligraphic", "angle", 30,
3362                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3363                                                               -90.0, 90.0, 1.0, 10.0,
3364                                                               labels, values, G_N_ELEMENTS(labels),
3365                                                               sp_ddc_angle_value_changed, 1, 0 );
3366         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3367         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3368         calligraphy_angle = eact;
3369         }
3371         {
3372         /* Fixation */
3373             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3374         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3375         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3376                                                               _("Fixation"), _("Fixation:"),
3377                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3378                                                               "tools.calligraphic", "flatness", 0.9,
3379                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3380                                                               0.0, 1.0, 0.01, 0.1,
3381                                                               labels, values, G_N_ELEMENTS(labels),
3382                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3383         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3384         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3385         }
3387         {
3388         /* Cap Rounding */
3389             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3390         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3391         // TRANSLATORS: "cap" means "end" (both start and finish) here
3392         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3393                                                               _("Cap rounding"), _("Caps:"),
3394                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3395                                                               "tools.calligraphic", "cap_rounding", 0.0,
3396                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3397                                                               0.0, 5.0, 0.01, 0.1,
3398                                                               labels, values, G_N_ELEMENTS(labels),
3399                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3400         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3401         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3402         }
3404         {
3405         /* Tremor */
3406             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3407         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3408         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3409                                                               _("Stroke Tremor"), _("Tremor:"),
3410                                                               _("Increase to make strokes rugged and trembling"),
3411                                                               "tools.calligraphic", "tremor", 0.0,
3412                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3413                                                               0.0, 1.0, 0.01, 0.1,
3414                                                               labels, values, G_N_ELEMENTS(labels),
3415                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3417         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3418         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3419         }
3421         {
3422         /* Wiggle */
3423         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3424         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3425         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3426                                                               _("Pen Wiggle"), _("Wiggle:"),
3427                                                               _("Increase to make the pen waver and wiggle"),
3428                                                               "tools.calligraphic", "wiggle", 0.0,
3429                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3430                                                               0.0, 1.0, 0.01, 0.1,
3431                                                               labels, values, G_N_ELEMENTS(labels),
3432                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3433         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3434         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3435         }
3437         {
3438         /* Mass */
3439             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3440         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3441         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3442                                                               _("Pen Mass"), _("Mass:"),
3443                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3444                                                               "tools.calligraphic", "mass", 0.02,
3445                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3446                                                               0.0, 1.0, 0.01, 0.1,
3447                                                               labels, values, G_N_ELEMENTS(labels),
3448                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3449         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3450         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3451         }
3454         /* Trace Background button */
3455         {
3456             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3457                                                           _("Trace Background"),
3458                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3459                                                           "trace_background",
3460                                                           Inkscape::ICON_SIZE_DECORATION );
3461             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3462             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3463             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3464         }
3466         /* Use Pressure button */
3467         {
3468             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3469                                                           _("Pressure"),
3470                                                           _("Use the pressure of the input device to alter the width of the pen"),
3471                                                           "use_pressure",
3472                                                           Inkscape::ICON_SIZE_DECORATION );
3473             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3474             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3475             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3476         }
3478         /* Use Tilt button */
3479         {
3480             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3481                                                           _("Tilt"),
3482                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3483                                                           "use_tilt",
3484                                                           Inkscape::ICON_SIZE_DECORATION );
3485             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3486             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3487             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3488             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3489         }
3491         /* Reset */
3492         {
3493             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3494                                              _("Defaults"),
3495                                              _("Reset all parameters to defaults"),
3496                                              GTK_STOCK_CLEAR );
3497             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3498             gtk_action_group_add_action( mainActions, act );
3499             gtk_action_set_sensitive( act, TRUE );
3500         }
3501     }
3505 //########################
3506 //##    Circle / Arc    ##
3507 //########################
3509 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3511     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3512     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3514     if (v1 == 0 && v2 == 0) {
3515         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3516             gtk_action_set_sensitive( ocb, FALSE );
3517             gtk_action_set_sensitive( make_whole, FALSE );
3518         }
3519     } else {
3520         gtk_action_set_sensitive( ocb, TRUE );
3521         gtk_action_set_sensitive( make_whole, TRUE );
3522     }
3525 static void
3526 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3528     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3530     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3531         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3532     }
3534     // quit if run by the attr_changed listener
3535     if (g_object_get_data( tbl, "freeze" )) {
3536         return;
3537     }
3539     // in turn, prevent listener from responding
3540     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3542     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3544     bool modmade = false;
3545     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3546          items != NULL;
3547          items = items->next)
3548     {
3549         SPItem *item = SP_ITEM(items->data);
3551         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3553             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3554             SPArc *arc = SP_ARC(item);
3556             if (!strcmp(value_name, "start"))
3557                 ge->start = (adj->value * M_PI)/ 180;
3558             else
3559                 ge->end = (adj->value * M_PI)/ 180;
3561             sp_genericellipse_normalize(ge);
3562             ((SPObject *)arc)->updateRepr();
3563             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3565             modmade = true;
3566         }
3567     }
3569     g_free(namespaced_name);
3571     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3573     sp_arctb_sensitivize( tbl, adj->value, other->value );
3575     if (modmade) {
3576         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3577                                    _("Arc: Change start/end"));
3578     }
3580     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3584 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3586     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3589 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3591     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3594 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3596     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3597     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3598         if ( ege_select_one_action_get_active( act ) != 0 ) {
3599             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3600         } else {
3601             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3602         }
3603     }
3605     // quit if run by the attr_changed listener
3606     if (g_object_get_data( tbl, "freeze" )) {
3607         return;
3608     }
3610     // in turn, prevent listener from responding
3611     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3613     bool modmade = false;
3615     if ( ege_select_one_action_get_active(act) != 0 ) {
3616         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3617              items != NULL;
3618              items = items->next)
3619         {
3620             if (SP_IS_ARC((SPItem *) items->data)) {
3621                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3622                 repr->setAttribute("sodipodi:open", "true");
3623                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3624                 modmade = true;
3625             }
3626         }
3627     } else {
3628         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3629              items != NULL;
3630              items = items->next)
3631         {
3632             if (SP_IS_ARC((SPItem *) items->data))    {
3633                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3634                 repr->setAttribute("sodipodi:open", NULL);
3635                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3636                 modmade = true;
3637             }
3638         }
3639     }
3641     if (modmade) {
3642         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3643                                    _("Arc: Change open/closed"));
3644     }
3646     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3649 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3651     GtkAdjustment *adj;
3652     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3653     gtk_adjustment_set_value(adj, 0.0);
3654     gtk_adjustment_value_changed(adj);
3656     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3657     gtk_adjustment_set_value(adj, 0.0);
3658     gtk_adjustment_value_changed(adj);
3660     spinbutton_defocus( GTK_OBJECT(obj) );
3663 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3664                                       gchar const */*old_value*/, gchar const */*new_value*/,
3665                                       bool /*is_interactive*/, gpointer data)
3667     GObject *tbl = G_OBJECT(data);
3669     // quit if run by the _changed callbacks
3670     if (g_object_get_data( tbl, "freeze" )) {
3671         return;
3672     }
3674     // in turn, prevent callbacks from responding
3675     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3677     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3678     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3680     GtkAdjustment *adj1,*adj2;
3681     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3682     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3683     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3684     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3686     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3688     char const *openstr = NULL;
3689     openstr = repr->attribute("sodipodi:open");
3690     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3692     if (openstr) {
3693         ege_select_one_action_set_active( ocb, 1 );
3694     } else {
3695         ege_select_one_action_set_active( ocb, 0 );
3696     }
3698     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3701 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3702     NULL, /* child_added */
3703     NULL, /* child_removed */
3704     arc_tb_event_attr_changed,
3705     NULL, /* content_changed */
3706     NULL  /* order_changed */
3707 };
3710 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3712     int n_selected = 0;
3713     Inkscape::XML::Node *repr = NULL;
3715     purge_repr_listener( tbl, tbl );
3717     for (GSList const *items = selection->itemList();
3718          items != NULL;
3719          items = items->next)
3720     {
3721         if (SP_IS_ARC((SPItem *) items->data)) {
3722             n_selected++;
3723             repr = SP_OBJECT_REPR((SPItem *) items->data);
3724         }
3725     }
3727     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3729     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3730     if (n_selected == 0) {
3731         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3732     } else if (n_selected == 1) {
3733         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3734         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3736         if (repr) {
3737             g_object_set_data( tbl, "repr", repr );
3738             Inkscape::GC::anchor(repr);
3739             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3740             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3741         }
3742     } else {
3743         // FIXME: implement averaging of all parameters for multiple selected
3744         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3745         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3746         sp_arctb_sensitivize( tbl, 1, 0 );
3747     }
3751 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3753     EgeAdjustmentAction* eact = 0;
3756     {
3757         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3758         ege_output_action_set_use_markup( act, TRUE );
3759         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3760         g_object_set_data( holder, "mode_action", act );
3761     }
3763     /* Start */
3764     {
3765         eact = create_adjustment_action( "ArcStartAction",
3766                                          _("Start"), _("Start:"),
3767                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3768                                          "tools.shapes.arc", "start", 0.0,
3769                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3770                                          -360.0, 360.0, 1.0, 10.0,
3771                                          0, 0, 0,
3772                                          sp_arctb_start_value_changed);
3773         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3774     }
3776     /* End */
3777     {
3778         eact = create_adjustment_action( "ArcEndAction",
3779                                          _("End"), _("End:"),
3780                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3781                                          "tools.shapes.arc", "end", 0.0,
3782                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3783                                          -360.0, 360.0, 1.0, 10.0,
3784                                          0, 0, 0,
3785                                          sp_arctb_end_value_changed);
3786         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3787     }
3789     /* Segments / Pie checkbox */
3790     {
3791         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3793         GtkTreeIter iter;
3794         gtk_list_store_append( model, &iter );
3795         gtk_list_store_set( model, &iter,
3796                             0, _("Closed arc"),
3797                             1, _("Switch to segment (closed shape with two radii)"),
3798                             2, "circle_closed_arc",
3799                             -1 );
3801         gtk_list_store_append( model, &iter );
3802         gtk_list_store_set( model, &iter,
3803                             0, _("Open Arc"),
3804                             1, _("Switch to arc (unclosed shape)"),
3805                             2, "circle_open_arc",
3806                             -1 );
3808         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3809         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3810         g_object_set_data( holder, "open_action", act );
3812         ege_select_one_action_set_appearance( act, "full" );
3813         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3814         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3815         ege_select_one_action_set_icon_column( act, 2 );
3816         ege_select_one_action_set_tooltip_column( act, 1  );
3818         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3819         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3820         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3821         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3822     }
3824     /* Make Whole */
3825     {
3826         InkAction* inky = ink_action_new( "ArcResetAction",
3827                                           _("Make whole"),
3828                                           _("Make the shape a whole ellipse, not arc or segment"),
3829                                           "reset_circle",
3830                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3831         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3832         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3833         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3834         g_object_set_data( holder, "make_whole", inky );
3835     }
3837     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3838     // sensitivize make whole and open checkbox
3839     {
3840         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3841         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3842         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3843     }
3846     sigc::connection *connection = new sigc::connection(
3847         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3848         );
3849     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3850     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3856 // toggle button callbacks and updaters
3858 //########################
3859 //##      Dropper       ##
3860 //########################
3862 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3863     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3864     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3865     if ( set_action ) {
3866         if ( gtk_toggle_action_get_active( act ) ) {
3867             gtk_action_set_sensitive( set_action, TRUE );
3868         } else {
3869             gtk_action_set_sensitive( set_action, FALSE );
3870         }
3871     }
3873     spinbutton_defocus(GTK_OBJECT(tbl));
3876 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3877     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3878     spinbutton_defocus(GTK_OBJECT(tbl));
3882 /**
3883  * Dropper auxiliary toolbar construction and setup.
3884  *
3885  * TODO: Would like to add swatch of current color.
3886  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3887  *       can drag and drop places. Will provide a nice mixing palette.
3888  */
3889 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3891     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3893     {
3894         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3895                                                       _("Pick alpha"),
3896                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3897                                                       "color_alpha_get",
3898                                                       Inkscape::ICON_SIZE_DECORATION );
3899         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3900         g_object_set_data( holder, "pick_action", act );
3901         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3902         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3903     }
3905     {
3906         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3907                                                       _("Set alpha"),
3908                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3909                                                       "color_alpha_set",
3910                                                       Inkscape::ICON_SIZE_DECORATION );
3911         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3912         g_object_set_data( holder, "set_action", act );
3913         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3914         // make sure it's disabled if we're not picking alpha
3915         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3916         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3917     }
3921 //########################
3922 //##    Text Toolbox    ##
3923 //########################
3924 /*
3925 static void
3926 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3928     //Call back for letter sizing spinbutton
3931 static void
3932 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3934     //Call back for line height spinbutton
3937 static void
3938 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3940     //Call back for horizontal kerning spinbutton
3943 static void
3944 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3946     //Call back for vertical kerning spinbutton
3949 static void
3950 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3952     //Call back for letter rotation spinbutton
3953 }*/
3955 namespace {
3957 bool visible = false;
3959 void
3960 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3962     SPStyle *query =
3963         sp_style_new (SP_ACTIVE_DOCUMENT);
3964     
3965     int result_fontspec =
3966         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3968     int result_family =
3969         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3971     int result_style =
3972         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3974     int result_numbers =
3975         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3977     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3979     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3980     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3981     {
3982         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3984         if (repr)
3985         {
3986             sp_style_read_from_repr (query, repr);
3987         }
3988         else
3989         {
3990             return;
3991         }
3992     }
3994     if (query->text)
3995     {
3996         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3997             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3998             gtk_entry_set_text (GTK_ENTRY (entry), "");
4000         } else if (query->text->font_specification.value || query->text->font_family.value) {
4002             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4003             
4004             // Get the font that corresponds 
4005             Glib::ustring familyName;
4006             
4007             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4008             if (font) {
4009                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4010                 font->Unref();
4011                 font = NULL;
4012             }
4013             
4014             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4016             Gtk::TreePath path;
4017             try {
4018                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4019             } catch (...) {
4020                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4021                 return;
4022             }
4024             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4025             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4027             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4029             gtk_tree_selection_select_path (tselection, path.gobj());
4030             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4032             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4033         }
4035         //Size
4036         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4037         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4038         g_object_set_data (tbl, "size-block", gpointer(1));
4039         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4040         g_object_set_data (tbl, "size-block", gpointer(0));
4041         free (str);
4043         //Anchor
4044         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4045         {
4046             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4047             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4048             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4049             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4050         }
4051         else
4052         {
4053             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4054             {
4055                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4056                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4057                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4058                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4059             }
4060             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4061             {
4062                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4063                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4064                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4065                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4066             }
4067             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4068             {
4069                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4070                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4071                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4072                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4073             }
4074         }
4076         //Style
4077         {
4078             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4080             gboolean active = gtk_toggle_button_get_active (button);
4081             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4083             if (active != check)
4084             {
4085                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4086                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4087                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4088             }
4089         }
4091         {
4092             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4094             gboolean active = gtk_toggle_button_get_active (button);
4095             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4097             if (active != check)
4098             {
4099                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4100                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4101                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4102             }
4103         }
4105         //Orientation
4106         //locking both buttons, changing one affect all group (both)
4107         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4108         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4110         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4111         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4113         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4114         {
4115             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4116         }
4117         else
4118         {
4119             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4120         }
4121         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4122         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4123     }
4125     sp_style_unref(query);
4128 void
4129 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4131     sp_text_toolbox_selection_changed (selection, tbl);
4134 void
4135 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4137     sp_text_toolbox_selection_changed (NULL, tbl);
4140 void
4141 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4142                                 GObject             *tbl)
4144     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4145     GtkTreeModel *model = 0;
4146     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4147     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4148     GtkTreeIter   iter;
4149     char         *family = 0;
4151     (void)popdown;
4153     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4154     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4156     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4157         return;
4158     }
4160     gtk_tree_model_get (model, &iter, 0, &family, -1);
4162     if (g_object_get_data (G_OBJECT (selection), "block"))
4163     {
4164         gtk_entry_set_text (GTK_ENTRY (entry), family);
4165         return;
4166     }
4168     gtk_entry_set_text (GTK_ENTRY (entry), family);
4170     SPStyle *query =
4171         sp_style_new (SP_ACTIVE_DOCUMENT);
4173     int result_fontspec =
4174         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4175     
4176     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4177     
4178     SPCSSAttr *css = sp_repr_css_attr_new ();
4179     
4180     
4181     // First try to get the font spec from the stored value
4182     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4183     
4184     if (fontSpec.empty()) {
4185         // Construct a new font specification if it does not yet exist
4186         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4187         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4188         fontFromStyle->Unref();
4189     }
4190     
4191     if (!fontSpec.empty()) {
4192         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4193         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4194             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4195             if (font) {
4196                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4197                 
4198                 // Set all the these just in case they were altered when finding the best
4199                 // match for the new family and old style...
4200                 
4201                 gchar c[256];
4202                 
4203                 font->Family(c, 256);
4204                 sp_repr_css_set_property (css, "font-family", c);
4205                 
4206                 font->Attribute( "weight", c, 256);
4207                 sp_repr_css_set_property (css, "font-weight", c);
4208                 
4209                 font->Attribute("style", c, 256);
4210                 sp_repr_css_set_property (css, "font-style", c);
4211                 
4212                 font->Attribute("stretch", c, 256);
4213                 sp_repr_css_set_property (css, "font-stretch", c);
4214                 
4215                 font->Attribute("variant", c, 256);
4216                 sp_repr_css_set_property (css, "font-variant", c);
4217                 
4218                 font->Unref();
4219             }
4220         }
4221     }
4223     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4224     if (result_fontspec == QUERY_STYLE_NOTHING)
4225     {
4226         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4227         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4228     }
4229     else
4230     {
4231         sp_desktop_set_style (desktop, css, true, true);
4232     }
4234     sp_style_unref(query);
4236     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4237                                    _("Text: Change font family"));
4238     sp_repr_css_attr_unref (css);
4239     free (family);
4240     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4242     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4245 void
4246 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4247                                        GObject      *tbl)
4249     const char *family = gtk_entry_get_text (entry);
4251     try {
4252         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4253         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4254         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4255         gtk_tree_selection_select_path (selection, path.gobj());
4256         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4257         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4258     } catch (...) {
4259         if (family && strlen (family))
4260         {
4261             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4262         }
4263     }
4266 void
4267 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4268                                    gpointer          data)
4270     if (g_object_get_data (G_OBJECT (button), "block")) return;
4271     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4272     int prop = GPOINTER_TO_INT(data);
4274     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4275     SPCSSAttr *css = sp_repr_css_attr_new ();
4277     switch (prop)
4278     {
4279         case 0:
4280         {
4281             sp_repr_css_set_property (css, "text-anchor", "start");
4282             sp_repr_css_set_property (css, "text-align", "start");
4283             break;
4284         }
4285         case 1:
4286         {
4287             sp_repr_css_set_property (css, "text-anchor", "middle");
4288             sp_repr_css_set_property (css, "text-align", "center");
4289             break;
4290         }
4292         case 2:
4293         {
4294             sp_repr_css_set_property (css, "text-anchor", "end");
4295             sp_repr_css_set_property (css, "text-align", "end");
4296             break;
4297         }
4299         case 3:
4300         {
4301             sp_repr_css_set_property (css, "text-anchor", "start");
4302             sp_repr_css_set_property (css, "text-align", "justify");
4303             break;
4304         }
4305     }
4307     SPStyle *query =
4308         sp_style_new (SP_ACTIVE_DOCUMENT);
4309     int result_numbers =
4310         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4312     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4313     if (result_numbers == QUERY_STYLE_NOTHING)
4314     {
4315         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4316     }
4318     sp_style_unref(query);
4320     sp_desktop_set_style (desktop, css, true, true);
4321     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4322                                    _("Text: Change alignment"));
4323     sp_repr_css_attr_unref (css);
4325     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4328 void
4329 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4330                                gpointer          data)
4332     if (g_object_get_data (G_OBJECT (button), "block")) return;
4334     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4335     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4336     int          prop       = GPOINTER_TO_INT(data);
4337     bool         active     = gtk_toggle_button_get_active (button);
4339     SPStyle *query =
4340         sp_style_new (SP_ACTIVE_DOCUMENT);
4341     
4342     int result_fontspec =
4343         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4345     int result_family =
4346         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4348     int result_style =
4349         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4351     int result_numbers =
4352         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4353     
4354     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4355     Glib::ustring newFontSpec = "";
4356     
4357     if (fontSpec.empty()) {
4358         // Construct a new font specification if it does not yet exist
4359         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4360         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4361         fontFromStyle->Unref();
4362     }
4363     
4364     switch (prop)
4365     {
4366         case 0:
4367         {
4368             if (!fontSpec.empty()) {
4369                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4370             }
4371             if (fontSpec != newFontSpec) {
4372                 // Don't even set the bold if the font didn't exist on the system
4373                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4374             }
4375             break;
4376         }
4378         case 1:
4379         {
4380             if (!fontSpec.empty()) {
4381                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4382             }
4383             if (fontSpec != newFontSpec) {
4384                 // Don't even set the italic if the font didn't exist on the system
4385                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4386             }
4387             break;
4388         }
4389     }
4391     if (!newFontSpec.empty()) {
4392         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str()); 
4393     }
4395     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4396     if (result_fontspec == QUERY_STYLE_NOTHING)
4397     {
4398         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4399     }
4401     sp_style_unref(query);
4403     sp_desktop_set_style (desktop, css, true, true);
4404     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4405                                    _("Text: Change font style"));
4406     sp_repr_css_attr_unref (css);
4408     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4411 void
4412 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4413                                      gpointer         data)
4415     if (g_object_get_data (G_OBJECT (button), "block")) {
4416         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4417         return;
4418     }
4420     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4421     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4422     int          prop       = GPOINTER_TO_INT(data);
4424     switch (prop)
4425     {
4426         case 0:
4427         {
4428             sp_repr_css_set_property (css, "writing-mode", "lr");
4429             break;
4430         }
4432         case 1:
4433         {
4434             sp_repr_css_set_property (css, "writing-mode", "tb");
4435             break;
4436         }
4437     }
4439     SPStyle *query =
4440         sp_style_new (SP_ACTIVE_DOCUMENT);
4441     int result_numbers =
4442         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4444     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4445     if (result_numbers == QUERY_STYLE_NOTHING)
4446     {
4447         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4448     }
4450     sp_desktop_set_style (desktop, css, true, true);
4451     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4452                                    _("Text: Change orientation"));
4453     sp_repr_css_attr_unref (css);
4455     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4458 gboolean
4459 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4461     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4462     if (!desktop) return FALSE;
4464     switch (get_group0_keyval (event)) {
4465         case GDK_Escape: // defocus
4466             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4467             sp_text_toolbox_selection_changed (NULL, tbl); // update
4468             return TRUE; // I consumed the event
4469             break;
4470     }
4471     return FALSE;
4474 gboolean
4475 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4477     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4478     if (!desktop) return FALSE;
4480     switch (get_group0_keyval (event)) {
4481         case GDK_KP_Enter:
4482         case GDK_Return:
4483         case GDK_Escape: // defocus
4484             gtk_widget_hide (w);
4485             visible = false;
4486             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4487             return TRUE; // I consumed the event
4488             break;
4489     }
4490     return FALSE;
4494 void
4495 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4496                                GObject     *tbl)
4498     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4500     if (g_object_get_data (tbl, "size-block")) return;
4502     // If this is not from selecting a size in the list (in which case get_active will give the
4503     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4504     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4505     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4506     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4507         return;
4509     gchar *endptr;
4510     gdouble value = -1;
4511     char *text = gtk_combo_box_get_active_text (cbox);
4512     if (text) {
4513         value = g_strtod (text, &endptr);
4514         if (endptr == text) // conversion failed, non-numeric input
4515             value = -1;
4516         free (text);
4517     }
4518     if (value <= 0) {
4519         return; // could not parse value 
4520     }
4522     SPCSSAttr *css = sp_repr_css_attr_new ();
4523     Inkscape::CSSOStringStream osfs;
4524     osfs << value;
4525     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4527     SPStyle *query =
4528         sp_style_new (SP_ACTIVE_DOCUMENT);
4529     int result_numbers =
4530         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4532     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4533     if (result_numbers == QUERY_STYLE_NOTHING)
4534     {
4535         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4536     }
4538     sp_style_unref(query);
4540     sp_desktop_set_style (desktop, css, true, true);
4541     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4542                                    _("Text: Change font size"));
4543     sp_repr_css_attr_unref (css);
4545     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4548 gboolean
4549 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus *event, GObject *tbl)
4551     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4552     if (!desktop) return FALSE;
4554     if (!g_object_get_data (tbl, "esc-pressed")) {
4555         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4556         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4557         sp_text_toolbox_size_changed (cbox, tbl);
4558         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4559     }
4560     return FALSE; // I consumed the event
4564 gboolean
4565 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4567     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4568     if (!desktop) return FALSE;
4570     switch (get_group0_keyval (event)) {
4571         case GDK_Escape: // defocus
4572             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4573             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4574             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4575             return TRUE; // I consumed the event
4576             break;
4577         case GDK_Return: // defocus
4578         case GDK_KP_Enter:
4579             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4580             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4581             sp_text_toolbox_size_changed (cbox, tbl);
4582             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4583             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4584             return TRUE; // I consumed the event
4585             break;
4586     }
4587     return FALSE;
4590 void
4591 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4592                                          GObject            *tbl)
4594     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4595     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4596     int x, y;
4598     if (!visible)
4599     {
4600         gdk_window_get_origin (widget->window, &x, &y);
4601         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4602         gtk_widget_show_all (popdown);
4604         gdk_pointer_grab (widget->window, TRUE,
4605                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4606                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4607                                         GDK_POINTER_MOTION_MASK),
4608                           NULL, NULL, GDK_CURRENT_TIME);
4610         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4612         visible = true;
4613     }
4614     else
4615     {
4616         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4617         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4618         gtk_widget_hide (popdown);
4619         visible = false;
4620     }
4623 gboolean
4624 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4625                                  GdkEventFocus    */*event*/,
4626                                  GObject          */*tbl*/)
4628     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4629     return FALSE;
4632 gboolean
4633 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4634                                    GdkEventFocus    */*event*/,
4635                                    GObject          */*tbl*/)
4637     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4639     gtk_widget_hide (popdown);
4640     visible = false;
4641     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4642     return TRUE;
4645 void
4646 cell_data_func  (GtkTreeViewColumn */*column*/,
4647                  GtkCellRenderer   *cell,
4648                  GtkTreeModel      *tree_model,
4649                  GtkTreeIter       *iter,
4650                  gpointer           /*data*/)
4652     char        *family,
4653         *family_escaped,
4654         *sample_escaped;
4656     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4658     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4660     family_escaped = g_markup_escape_text (family, -1);
4661     sample_escaped = g_markup_escape_text (sample, -1);
4663     std::stringstream markup;
4664     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4665     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4667     free (family);
4668     free (family_escaped);
4669     free (sample_escaped);
4672 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4673     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4674     if (completion) {
4675         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4676         g_object_unref (completion);
4677     }
4680 GtkWidget*
4681 sp_text_toolbox_new (SPDesktop *desktop)
4683     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4685     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4686     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4688     GtkTooltips *tt = gtk_tooltips_new();
4689     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4691     ////////////Family
4692     //Window
4693     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4694     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4696     //Entry
4697     GtkWidget           *entry = gtk_entry_new ();
4698     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4699     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4700     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4701     gtk_entry_completion_set_text_column (completion, 0);
4702     gtk_entry_completion_set_minimum_key_length (completion, 1);
4703     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4704     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4705     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4706     aux_toolbox_space (tbl, 1);
4707     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4708     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4710     //Button
4711     GtkWidget   *button = gtk_button_new ();
4712     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4713     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4715     //Popdown
4716     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4717     GtkWidget           *treeview = gtk_tree_view_new ();
4719     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4720     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4721     gtk_tree_view_column_pack_start (column, cell, FALSE);
4722     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4723     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4724     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4726     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4727     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4728     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4730     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4732     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4733     gtk_container_add (GTK_CONTAINER (sw), treeview);
4735     gtk_container_add (GTK_CONTAINER (window), sw);
4736     gtk_widget_set_size_request (window, 300, 450);
4738     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4739     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4740     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4742     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4744     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4745     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4747     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4748     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4750     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4751     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4752     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4753     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4754     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4756     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4757     aux_toolbox_space (tbl, 1);
4758     GtkWidget *box = gtk_event_box_new ();
4759     gtk_container_add (GTK_CONTAINER (box), image);
4760     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4761     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4762     GtkTooltips *tooltips = gtk_tooltips_new ();
4763     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4764     gtk_widget_hide (GTK_WIDGET (box));
4765     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4767     ////////////Size
4768     const char *sizes[] = {
4769         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4770         "16", "18", "20", "22", "24", "28",
4771         "32", "36", "40", "48", "56", "64", "72", "144"
4772     };
4774     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4775     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4776     gtk_widget_set_size_request (cbox, 80, -1);
4777     aux_toolbox_space (tbl, 1);
4778     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4779     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4780     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4781     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4782     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4784     //spacer
4785     aux_toolbox_space (tbl, 4);
4786     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4788     ////////////Text anchor
4789     GtkWidget *group   = gtk_radio_button_new (NULL);
4790     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4791     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4793     // left
4794     GtkWidget *rbutton = group;
4795     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4796     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4797     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4799     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4800     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4801     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4802     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4804     // center
4805     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4806     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4807     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4808     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4810     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4811     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4812     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4813     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4815     // right
4816     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4817     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4818     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4819     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4821     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4822     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4823     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4824     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4826     // fill
4827     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4828     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4829     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4830     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4832     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4833     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4834     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4835     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4837     aux_toolbox_space (tbl, 1);
4838     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4840     //spacer
4841     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4843     ////////////Text style
4844     row = gtk_hbox_new (FALSE, 4);
4846     // bold
4847     rbutton = gtk_toggle_button_new ();
4848     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4849     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4850     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4851     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4853     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4854     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4855     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4857     // italic
4858     rbutton = gtk_toggle_button_new ();
4859     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4860     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4861     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4862     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4864     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4865     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4866     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4868     aux_toolbox_space (tbl, 1);
4869     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4871     //spacer
4872     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4874     ////////////Text orientation
4875     group   = gtk_radio_button_new (NULL);
4876     row     = gtk_hbox_new (FALSE, 4);
4877     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4879     // horizontal
4880     rbutton = group;
4881     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4882     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4883     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4884     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4886     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4887     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4888     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4890     // vertical
4891     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4892     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4893     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4894     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4895     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4897     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4898     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4899     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4900     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4903     //watch selection
4904     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4906     sigc::connection *c_selection_changed =
4907         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4908                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4909     pool->add_connection ("selection-changed", c_selection_changed);
4911     sigc::connection *c_selection_modified =
4912         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4913                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4914     pool->add_connection ("selection-modified", c_selection_modified);
4916     sigc::connection *c_subselection_changed =
4917         new sigc::connection (desktop->connectToolSubselectionChanged
4918                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4919     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4921     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4924     gtk_widget_show_all (tbl);
4925     return tbl;
4927 } // end of sp_text_toolbox_new()
4929 }//<unnamed> namespace
4932 //#########################
4933 //##      Connector      ##
4934 //#########################
4936 static void sp_connector_path_set_avoid(void)
4938     cc_selection_set_avoid(true);
4942 static void sp_connector_path_set_ignore(void)
4944     cc_selection_set_avoid(false);
4949 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4951     // quit if run by the _changed callbacks
4952     if (g_object_get_data( tbl, "freeze" )) {
4953         return;
4954     }
4956     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4957     SPDocument *doc = sp_desktop_document(desktop);
4959     if (!sp_document_get_undo_sensitive(doc))
4960     {
4961         return;
4962     }
4964     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4966     if ( repr->attribute("inkscape:connector-spacing") ) {
4967         gdouble priorValue = gtk_adjustment_get_value(adj);
4968         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4969         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4970             return;
4971         }
4972     } else if ( adj->value == defaultConnSpacing ) {
4973         return;
4974     }
4976     // in turn, prevent callbacks from responding
4977     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4979     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4980     SP_OBJECT(desktop->namedview)->updateRepr();
4982     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4983     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4984         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4985         NR::Matrix m = NR::identity();
4986         avoid_item_move(&m, item);
4987     }
4989     if (items) {
4990         g_slist_free(items);
4991     }
4993     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4994             _("Change connector spacing"));
4996     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4998     spinbutton_defocus(GTK_OBJECT(tbl));
5001 static void sp_connector_graph_layout(void)
5003     if (!SP_ACTIVE_DESKTOP) return;
5005     // hack for clones, see comment in align-and-distribute.cpp
5006     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5007     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5009     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5011     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5013     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5016 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5018     if ( gtk_toggle_action_get_active( act ) ) {
5019         prefs_set_string_attribute("tools.connector", "directedlayout",
5020                 "true");
5021     } else {
5022         prefs_set_string_attribute("tools.connector", "directedlayout",
5023                 "false");
5024     }
5027 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5029     if ( gtk_toggle_action_get_active( act ) ) {
5030         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5031                 "true");
5032     } else {
5033         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5034                 "false");
5035     }
5039 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5041     prefs_set_double_attribute("tools.connector", "length", adj->value);
5042     spinbutton_defocus(GTK_OBJECT(tbl));
5045 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5046                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5047                                             bool /*is_interactive*/, gpointer data)
5049     GtkWidget *tbl = GTK_WIDGET(data);
5051     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5052         return;
5053     }
5054     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5055         return;
5056     }
5058     GtkAdjustment *adj = (GtkAdjustment*)
5059             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5060     gdouble spacing = defaultConnSpacing;
5061     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5063     gtk_adjustment_set_value(adj, spacing);
5067 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5068     NULL, /* child_added */
5069     NULL, /* child_removed */
5070     connector_tb_event_attr_changed,
5071     NULL, /* content_changed */
5072     NULL  /* order_changed */
5073 };
5076 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5078     {
5079         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5080                                           _("Avoid"),
5081                                           _("Make connectors avoid selected objects"),
5082                                           "connector_avoid",
5083                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5084         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5085         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5086     }
5088     {
5089         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5090                                           _("Ignore"),
5091                                           _("Make connectors ignore selected objects"),
5092                                           "connector_ignore",
5093                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5094         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5095         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5096     }
5098     EgeAdjustmentAction* eact = 0;
5100     // Spacing spinbox
5101     eact = create_adjustment_action( "ConnectorSpacingAction",
5102                                      _("Connector Spacing"), _("Spacing:"),
5103                                      _("The amount of space left around objects by auto-routing connectors"),
5104                                      "tools.connector", "spacing", defaultConnSpacing,
5105                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5106                                      0, 100, 1.0, 10.0,
5107                                      0, 0, 0,
5108                                      connector_spacing_changed, 1, 0 );
5109     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5111     // Graph (connector network) layout
5112     {
5113         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5114                                           _("Graph"),
5115                                           _("Nicely arrange selected connector network"),
5116                                           "graph_layout",
5117                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5118         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5119         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5120     }
5122     // Default connector length spinbox
5123     eact = create_adjustment_action( "ConnectorLengthAction",
5124                                      _("Connector Length"), _("Length:"),
5125                                      _("Ideal length for connectors when layout is applied"),
5126                                      "tools.connector", "length", 100,
5127                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5128                                      10, 1000, 10.0, 100.0,
5129                                      0, 0, 0,
5130                                      connector_length_changed, 1, 0 );
5131     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5134     // Directed edges toggle button
5135     {
5136         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5137                                                       _("Downwards"),
5138                                                       _("Make connectors with end-markers (arrows) point downwards"),
5139                                                       "directed_graph",
5140                                                       Inkscape::ICON_SIZE_DECORATION );
5141         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5143         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5144         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5145                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5147         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5148     }
5150     // Avoid overlaps toggle button
5151     {
5152         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5153                                                       _("Remove overlaps"),
5154                                                       _("Do not allow overlapping shapes"),
5155                                                       "remove_overlaps",
5156                                                       Inkscape::ICON_SIZE_DECORATION );
5157         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5159         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5160         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5161                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5163         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5164     }
5166     // Code to watch for changes to the connector-spacing attribute in
5167     // the XML.
5168     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5169     g_assert(repr != NULL);
5171     purge_repr_listener( holder, holder );
5173     if (repr) {
5174         g_object_set_data( holder, "repr", repr );
5175         Inkscape::GC::anchor(repr);
5176         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5177         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5178     }
5179 } // end of sp_connector_toolbox_prep()
5182 //#########################
5183 //##     Paintbucket     ##
5184 //#########################
5186 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5188     gint channels = ege_select_one_action_get_active( act );
5189     flood_channels_set_channels( channels );
5192 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5194     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5197 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5199     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5202 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5204     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5205     SPUnit const *unit = tracker->getActiveUnit();
5207     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5209     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5212 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5214     // FIXME: make defaults settable via Inkscape Options
5215     struct KeyValue {
5216         char const *key;
5217         double value;
5218     } const key_values[] = {
5219         {"threshold", 15},
5220         {"offset", 0.0}
5221     };
5223     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5224         KeyValue const &kv = key_values[i];
5225         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5226         if ( adj ) {
5227             gtk_adjustment_set_value(adj, kv.value);
5228         }
5229     }
5231     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5232     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5233     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5234     ege_select_one_action_set_active( autogap_action, 0 );
5237 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5239     EgeAdjustmentAction* eact = 0;
5241     {
5242         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5244         GList* items = 0;
5245         gint count = 0;
5246         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5247         {
5248             GtkTreeIter iter;
5249             gtk_list_store_append( model, &iter );
5250             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5251             count++;
5252         }
5253         g_list_free( items );
5254         items = 0;
5255         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5256         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5257         ege_select_one_action_set_appearance( act1, "compact" );
5258         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5259         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5260         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5261         g_object_set_data( holder, "channels_action", act1 );
5262     }
5264     // Spacing spinbox
5265     {
5266         eact = create_adjustment_action(
5267             "ThresholdAction",
5268             _("Fill Threshold"), _("Threshold:"),
5269             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5270             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5271             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5272             0, 0, 0,
5273             paintbucket_threshold_changed, 1, 0 );
5275         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5276     }
5278     // Create the units menu.
5279     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5280     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5281     g_object_set_data( holder, "tracker", tracker );
5282     {
5283         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5284         gtk_action_group_add_action( mainActions, act );
5285     }
5287     // Offset spinbox
5288     {
5289         eact = create_adjustment_action(
5290             "OffsetAction",
5291             _("Grow/shrink by"), _("Grow/shrink by:"),
5292             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5293             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5294             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5295             0, 0, 0,
5296             paintbucket_offset_changed, 1, 2);
5297         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5299         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5300     }
5302     /* Auto Gap */
5303     {
5304         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5306         GList* items = 0;
5307         gint count = 0;
5308         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5309         {
5310             GtkTreeIter iter;
5311             gtk_list_store_append( model, &iter );
5312             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5313             count++;
5314         }
5315         g_list_free( items );
5316         items = 0;
5317         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5318         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5319         ege_select_one_action_set_appearance( act2, "compact" );
5320         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5321         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5322         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5323         g_object_set_data( holder, "autogap_action", act2 );
5324     }
5326     /* Reset */
5327     {
5328         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5329                                           _("Defaults"),
5330                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5331                                           GTK_STOCK_CLEAR );
5332         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5333         gtk_action_group_add_action( mainActions, act );
5334         gtk_action_set_sensitive( act, TRUE );
5335     }
5339 /*
5340   Local Variables:
5341   mode:c++
5342   c-file-style:"stroustrup"
5343   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5344   indent-tabs-mode:nil
5345   fill-column:99
5346   End:
5347 */
5348 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :