Code

fix 189661
[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     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1721     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1723     if (!strcmp(name, "inkscape:randomized")) {
1724         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1725         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1726     } else if (!strcmp(name, "inkscape:rounded")) {
1727         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1728         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1729     } else if (!strcmp(name, "inkscape:flatsided")) {
1730         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1731         char const *flatsides = repr->attribute("inkscape:flatsided");
1732         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1733         if ( flatsides && !strcmp(flatsides,"false") ) {
1734             ege_select_one_action_set_active( flat_action, 1 );
1735             gtk_action_set_sensitive( prop_action, TRUE );
1736         } else {
1737             ege_select_one_action_set_active( flat_action, 0 );
1738             gtk_action_set_sensitive( prop_action, FALSE );
1739         }
1740     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1741         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1742         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1743         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1744         if (r2 < r1) {
1745             gtk_adjustment_set_value(adj, r2/r1);
1746         } else {
1747             gtk_adjustment_set_value(adj, r1/r2);
1748         }
1749     } else if (!strcmp(name, "sodipodi:sides")) {
1750         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1751         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1752     }
1754     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1758 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1760     NULL, /* child_added */
1761     NULL, /* child_removed */
1762     star_tb_event_attr_changed,
1763     NULL, /* content_changed */
1764     NULL  /* order_changed */
1765 };
1768 /**
1769  *  \param selection Should not be NULL.
1770  */
1771 static void
1772 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1774     int n_selected = 0;
1775     Inkscape::XML::Node *repr = NULL;
1777     purge_repr_listener( tbl, tbl );
1779     for (GSList const *items = selection->itemList();
1780          items != NULL;
1781          items = items->next)
1782     {
1783         if (SP_IS_STAR((SPItem *) items->data)) {
1784             n_selected++;
1785             repr = SP_OBJECT_REPR((SPItem *) items->data);
1786         }
1787     }
1789     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1791     if (n_selected == 0) {
1792         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1793     } else if (n_selected == 1) {
1794         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1796         if (repr) {
1797             g_object_set_data( tbl, "repr", repr );
1798             Inkscape::GC::anchor(repr);
1799             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1800             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1801         }
1802     } else {
1803         // FIXME: implement averaging of all parameters for multiple selected stars
1804         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1805         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1806     }
1810 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1812     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1813     // callbacks to lump all the changes for all selected objects in one undo step
1815     GtkAdjustment *adj = 0;
1817     // fixme: make settable in prefs!
1818     gint mag = 5;
1819     gdouble prop = 0.5;
1820     gboolean flat = FALSE;
1821     gdouble randomized = 0;
1822     gdouble rounded = 0;
1824     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1825     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1827     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1828     gtk_action_set_sensitive( sb2, !flat );
1830     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1831     gtk_adjustment_set_value(adj, mag);
1832     gtk_adjustment_value_changed(adj);
1834     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1835     gtk_adjustment_set_value(adj, prop);
1836     gtk_adjustment_value_changed(adj);
1838     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1839     gtk_adjustment_set_value(adj, rounded);
1840     gtk_adjustment_value_changed(adj);
1842     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1843     gtk_adjustment_set_value(adj, randomized);
1844     gtk_adjustment_value_changed(adj);
1848 void
1849 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1851     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1852     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1853     GtkWidget *l = gtk_label_new(NULL);
1854     gtk_label_set_markup(GTK_LABEL(l), title);
1855     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1856     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1857     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1861 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1863     {
1864         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1865         ege_output_action_set_use_markup( act, TRUE );
1866         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1867         g_object_set_data( holder, "mode_action", act );
1868     }
1870     {
1871         EgeAdjustmentAction* eact = 0;
1872         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1873         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1875         /* Flatsided checkbox */
1876         {
1877             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1879             GtkTreeIter iter;
1880             gtk_list_store_append( model, &iter );
1881             gtk_list_store_set( model, &iter,
1882                                 0, _("Polygon"),
1883                                 1, _("Regular polygon (with one handle) instead of a star"),
1884                                 2, "star_flat",
1885                                 -1 );
1887             gtk_list_store_append( model, &iter );
1888             gtk_list_store_set( model, &iter,
1889                                 0, _("Star"),
1890                                 1, _("Star instead of a regular polygon (with one handle)"),
1891                                 2, "star_angled",
1892                                 -1 );
1894             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1895             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1896             g_object_set_data( holder, "flat_action", act );
1898             ege_select_one_action_set_appearance( act, "full" );
1899             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1900             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1901             ege_select_one_action_set_icon_column( act, 2 );
1902             ege_select_one_action_set_tooltip_column( act, 1  );
1904             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1905             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1906         }
1908         /* Magnitude */
1909         {
1910         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1911         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1912         eact = create_adjustment_action( "MagnitudeAction",
1913                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1914                                          "tools.shapes.star", "magnitude", 3,
1915                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1916                                          3, 1024, 1, 5,
1917                                          labels, values, G_N_ELEMENTS(labels),
1918                                          sp_stb_magnitude_value_changed,
1919                                          1.0, 0 );
1920         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1921         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1922         }
1924         /* Spoke ratio */
1925         {
1926         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1927         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1928         eact = create_adjustment_action( "SpokeAction",
1929                                          _("Spoke ratio"), _("Spoke ratio:"),
1930                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1931                                          // Base radius is the same for the closest handle.
1932                                          _("Base radius to tip radius ratio"),
1933                                          "tools.shapes.star", "proportion", 0.5,
1934                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1935                                          0.01, 1.0, 0.01, 0.1,
1936                                          labels, values, G_N_ELEMENTS(labels),
1937                                          sp_stb_proportion_value_changed );
1938         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1939         g_object_set_data( holder, "prop_action", eact );
1940         }
1942         if ( !isFlatSided ) {
1943             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1944         } else {
1945             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1946         }
1948         /* Roundedness */
1949         {
1950         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1951         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1952         eact = create_adjustment_action( "RoundednessAction",
1953                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1954                                          "tools.shapes.star", "rounded", 0.0,
1955                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1956                                          -10.0, 10.0, 0.01, 0.1,
1957                                          labels, values, G_N_ELEMENTS(labels),
1958                                          sp_stb_rounded_value_changed );
1959         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1960         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1961         }
1963         /* Randomization */
1964         {
1965         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1966         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1967         eact = create_adjustment_action( "RandomizationAction",
1968                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1969                                          "tools.shapes.star", "randomized", 0.0,
1970                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1971                                          -10.0, 10.0, 0.001, 0.01,
1972                                          labels, values, G_N_ELEMENTS(labels),
1973                                          sp_stb_randomized_value_changed, 0.1, 3 );
1974         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1975         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1976         }
1977     }
1979     {
1980         /* Reset */
1981         {
1982             GtkAction* act = gtk_action_new( "StarResetAction",
1983                                              _("Defaults"),
1984                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1985                                              GTK_STOCK_CLEAR );
1986             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1987             gtk_action_group_add_action( mainActions, act );
1988             gtk_action_set_sensitive( act, TRUE );
1989         }
1990     }
1992     sigc::connection *connection = new sigc::connection(
1993         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1994         );
1995     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1996     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2000 //########################
2001 //##       Rect         ##
2002 //########################
2004 static void sp_rtb_sensitivize( GObject *tbl )
2006     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2007     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2008     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2010     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2011         gtk_action_set_sensitive( not_rounded, FALSE );
2012     } else {
2013         gtk_action_set_sensitive( not_rounded, TRUE );
2014     }
2018 static void
2019 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2020                           void (*setter)(SPRect *, gdouble))
2022     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2024     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2025     SPUnit const *unit = tracker->getActiveUnit();
2027     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2028         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2029     }
2031     // quit if run by the attr_changed listener
2032     if (g_object_get_data( tbl, "freeze" )) {
2033         return;
2034     }
2036     // in turn, prevent listener from responding
2037     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2039     bool modmade = false;
2040     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2041     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2042         if (SP_IS_RECT(items->data)) {
2043             if (adj->value != 0) {
2044                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2045             } else {
2046                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2047             }
2048             modmade = true;
2049         }
2050     }
2052     sp_rtb_sensitivize( tbl );
2054     if (modmade) {
2055         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2056                                    _("Change rectangle"));
2057     }
2059     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2062 static void
2063 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2065     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2068 static void
2069 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2071     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2074 static void
2075 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2077     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2080 static void
2081 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2083     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2088 static void
2089 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2091     GtkAdjustment *adj = 0;
2093     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2094     gtk_adjustment_set_value(adj, 0.0);
2095     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2096     gtk_adjustment_value_changed(adj);
2098     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2099     gtk_adjustment_set_value(adj, 0.0);
2100     gtk_adjustment_value_changed(adj);
2102     sp_rtb_sensitivize( obj );
2105 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2106                                        gchar const */*old_value*/, gchar const */*new_value*/,
2107                                        bool /*is_interactive*/, gpointer data)
2109     GObject *tbl = G_OBJECT(data);
2111     // quit if run by the _changed callbacks
2112     if (g_object_get_data( tbl, "freeze" )) {
2113         return;
2114     }
2116     // in turn, prevent callbacks from responding
2117     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2119     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2120     SPUnit const *unit = tracker->getActiveUnit();
2122     gpointer item = g_object_get_data( tbl, "item" );
2123     if (item && SP_IS_RECT(item)) {
2124         {
2125             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2126             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2127             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2128         }
2130         {
2131             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2132             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2133             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2134         }
2136         {
2137             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2138             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2139             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2140         }
2142         {
2143             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2144             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2145             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2146         }
2147     }
2149     sp_rtb_sensitivize( tbl );
2151     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2155 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2156     NULL, /* child_added */
2157     NULL, /* child_removed */
2158     rect_tb_event_attr_changed,
2159     NULL, /* content_changed */
2160     NULL  /* order_changed */
2161 };
2163 /**
2164  *  \param selection should not be NULL.
2165  */
2166 static void
2167 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2169     int n_selected = 0;
2170     Inkscape::XML::Node *repr = NULL;
2171     SPItem *item = NULL;
2173     if ( g_object_get_data( tbl, "repr" ) ) {
2174         g_object_set_data( tbl, "item", NULL );
2175     }
2176     purge_repr_listener( tbl, tbl );
2178     for (GSList const *items = selection->itemList();
2179          items != NULL;
2180          items = items->next) {
2181         if (SP_IS_RECT((SPItem *) items->data)) {
2182             n_selected++;
2183             item = (SPItem *) items->data;
2184             repr = SP_OBJECT_REPR(item);
2185         }
2186     }
2188     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2190     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2192     if (n_selected == 0) {
2193         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2195         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2196         gtk_action_set_sensitive(w, FALSE);
2197         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2198         gtk_action_set_sensitive(h, FALSE);
2200     } else if (n_selected == 1) {
2201         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2202         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2204         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2205         gtk_action_set_sensitive(w, TRUE);
2206         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2207         gtk_action_set_sensitive(h, TRUE);
2209         if (repr) {
2210             g_object_set_data( tbl, "repr", repr );
2211             g_object_set_data( tbl, "item", item );
2212             Inkscape::GC::anchor(repr);
2213             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2214             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2215         }
2216     } else {
2217         // FIXME: implement averaging of all parameters for multiple selected
2218         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2219         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2220         sp_rtb_sensitivize( tbl );
2221     }
2225 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2227     EgeAdjustmentAction* eact = 0;
2229     {
2230         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2231         ege_output_action_set_use_markup( act, TRUE );
2232         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2233         g_object_set_data( holder, "mode_action", act );
2234     }
2236     // rx/ry units menu: create
2237     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2238     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2239     // fixme: add % meaning per cent of the width/height
2240     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2241     g_object_set_data( holder, "tracker", tracker );
2243     /* W */
2244     {
2245         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2246         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2247         eact = create_adjustment_action( "RectWidthAction",
2248                                          _("Width"), _("W:"), _("Width of rectangle"),
2249                                          "tools.shapes.rect", "width", 0,
2250                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2251                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2252                                          labels, values, G_N_ELEMENTS(labels),
2253                                          sp_rtb_width_value_changed );
2254         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2255         g_object_set_data( holder, "width_action", eact );
2256         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2257         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2258     }
2260     /* H */
2261     {
2262         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2263         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2264         eact = create_adjustment_action( "RectHeightAction",
2265                                          _("Height"), _("H:"), _("Height of rectangle"),
2266                                          "tools.shapes.rect", "height", 0,
2267                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2268                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2269                                          labels, values, G_N_ELEMENTS(labels),
2270                                          sp_rtb_height_value_changed );
2271         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2272         g_object_set_data( holder, "height_action", eact );
2273         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2274         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2275     }
2277     /* rx */
2278     {
2279         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2280         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2281         eact = create_adjustment_action( "RadiusXAction",
2282                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2283                                          "tools.shapes.rect", "rx", 0,
2284                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2285                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2286                                          labels, values, G_N_ELEMENTS(labels),
2287                                          sp_rtb_rx_value_changed);
2288         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2289         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2290     }
2292     /* ry */
2293     {
2294         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2295         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2296         eact = create_adjustment_action( "RadiusYAction",
2297                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2298                                          "tools.shapes.rect", "ry", 0,
2299                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2300                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2301                                          labels, values, G_N_ELEMENTS(labels),
2302                                          sp_rtb_ry_value_changed);
2303         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2304         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2305     }
2307     // add the units menu
2308     {
2309         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2310         gtk_action_group_add_action( mainActions, act );
2311     }
2313     /* Reset */
2314     {
2315         InkAction* inky = ink_action_new( "RectResetAction",
2316                                           _("Not rounded"),
2317                                           _("Make corners sharp"),
2318                                           "squared_corner",
2319                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2320         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2321         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2322         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2323         g_object_set_data( holder, "not_rounded", inky );
2324     }
2326     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2327     sp_rtb_sensitivize( holder );
2329     sigc::connection *connection = new sigc::connection(
2330         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2331         );
2332     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2333     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2336 //########################
2337 //##       3D Box       ##
2338 //########################
2340 // normalize angle so that it lies in the interval [0,360]
2341 static double box3d_normalize_angle (double a) {
2342     double angle = a + ((int) (a/360.0))*360;
2343     if (angle < 0) {
2344         angle += 360.0;
2345     }
2346     return angle;
2349 static void
2350 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2351                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2352     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2353     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2354     //       are reset).
2355     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2357     if (is_infinite) {
2358         gtk_toggle_action_set_active(tact, TRUE);
2359         gtk_action_set_sensitive(act, TRUE);
2361         double angle = persp3d_get_infinite_angle(persp, axis);
2362         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2363             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2364         }
2365     } else {
2366         gtk_toggle_action_set_active(tact, FALSE);
2367         gtk_action_set_sensitive(act, FALSE);
2368     }
2371 static void
2372 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2373     if (!persp_repr) {
2374         g_print ("No perspective given to box3d_resync_toolbar().\n");
2375         return;
2376     }
2378     GtkWidget *tbl = GTK_WIDGET(data);
2379     GtkAdjustment *adj = 0;
2380     GtkAction *act = 0;
2381     GtkToggleAction *tact = 0;
2382     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2383     {
2384         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2385         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2386         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2388         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2389     }
2390     {
2391         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2392         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2393         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2395         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2396     }
2397     {
2398         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2399         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2400         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2402         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2403     }
2406 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2407                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2408                                                   bool /*is_interactive*/, gpointer data)
2410     GtkWidget *tbl = GTK_WIDGET(data);
2412     // quit if run by the attr_changed listener
2413     // note: it used to work without the differently called freeze_ attributes (here and in
2414     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2415     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2416         return;
2417     }
2419     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2420     // sp_document_maybe_done() when the document is undo insensitive)
2421     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2423     // TODO: Only update the appropriate part of the toolbar
2424 //    if (!strcmp(name, "inkscape:vp_z")) {
2425         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2426 //    }
2428     Persp3D *persp = persp3d_get_from_repr(repr);
2429     persp3d_update_box_reprs(persp);
2431     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2434 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2436     NULL, /* child_added */
2437     NULL, /* child_removed */
2438     box3d_persp_tb_event_attr_changed,
2439     NULL, /* content_changed */
2440     NULL  /* order_changed */
2441 };
2443 /**
2444  *  \param selection Should not be NULL.
2445  */
2446 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2447 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2448 static void
2449 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2451     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2452     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2453     // update the perspectives with infinite VPs and leave the other ones untouched).
2455     Inkscape::XML::Node *persp_repr = NULL;
2456     purge_repr_listener(tbl, tbl);
2458     SPItem *item = selection->singleItem();
2459     if (item && SP_IS_BOX3D(item)) {
2460         // FIXME: Also deal with multiple selected boxes
2461         SPBox3D *box = SP_BOX3D(item);
2462         Persp3D *persp = box3d_get_perspective(box);
2463         persp_repr = SP_OBJECT_REPR(persp);
2464         if (persp_repr) {
2465             g_object_set_data(tbl, "repr", persp_repr);
2466             Inkscape::GC::anchor(persp_repr);
2467             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2468             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2469         }
2471         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2472         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2473     
2474         box3d_resync_toolbar(persp_repr, tbl);
2475     }
2478 static void
2479 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2481     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2482     SPDocument *document = sp_desktop_document(desktop);
2484     // quit if run by the attr_changed listener
2485     // note: it used to work without the differently called freeze_ attributes (here and in
2486     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2487     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2488         return;
2489     }
2491     // in turn, prevent listener from responding
2492     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2494     //Persp3D *persp = document->current_persp3d;
2495     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2496     if (sel_persps.empty()) {
2497         // this can happen when the document is created; we silently ignore it
2498         return;
2499     }
2500     Persp3D *persp = *(sel_persps.begin());
2502     persp->tmat.set_infinite_direction (axis, adj->value);
2503     SP_OBJECT(persp)->updateRepr();
2505     // TODO: use the correct axis here, too
2506     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2508     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2512 static void
2513 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2515     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2518 static void
2519 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2521     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2524 static void
2525 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2527     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2531 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2533     // TODO: Take all selected perspectives into account
2534     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2535     if (sel_persps.empty()) {
2536         // this can happen when the document is created; we silently ignore it
2537         return;
2538     }
2539     Persp3D *persp = *(sel_persps.begin());
2541     bool set_infinite = gtk_toggle_action_get_active(act);
2542     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2545 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2547     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2550 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2552     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2555 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2557     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2560 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2562     EgeAdjustmentAction* eact = 0;
2563     SPDocument *document = sp_desktop_document (desktop);
2564     Persp3D *persp = document->current_persp3d;
2566     EgeAdjustmentAction* box3d_angle_x = 0;
2567     EgeAdjustmentAction* box3d_angle_y = 0;
2568     EgeAdjustmentAction* box3d_angle_z = 0;
2570     /* Angle X */
2571     {
2572         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2573         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2574         eact = create_adjustment_action( "3DBoxAngleXAction",
2575                                          _("Angle in X direction"), _("Angle X:"),
2576                                          // Translators: PL is short for 'perspective line'
2577                                          _("Angle of PLs in X direction"),
2578                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2579                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2580                                          -360.0, 360.0, 1.0, 10.0,
2581                                          labels, values, G_N_ELEMENTS(labels),
2582                                          box3d_angle_x_value_changed );
2583         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2584         g_object_set_data( holder, "box3d_angle_x_action", eact );
2585         box3d_angle_x = eact;
2586     }
2588     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2589         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2590     } else {
2591         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2592     }
2595     /* VP X state */
2596     {
2597         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2598                                                       // Translators: VP is short for 'vanishing point'
2599                                                       _("State of VP in X direction"),
2600                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2601                                                       "toggle_vp_x",
2602                                                       Inkscape::ICON_SIZE_DECORATION );
2603         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2604         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2605         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2606         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2607         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2608     }
2610     /* Angle Y */
2611     {
2612         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2613         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2614         eact = create_adjustment_action( "3DBoxAngleYAction",
2615                                          _("Angle in Y direction"), _("Angle Y:"),
2616                                          // Translators: PL is short for 'perspective line'
2617                                          _("Angle of PLs in Y direction"),
2618                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2619                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2620                                          -360.0, 360.0, 1.0, 10.0,
2621                                          labels, values, G_N_ELEMENTS(labels),
2622                                          box3d_angle_y_value_changed );
2623         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2624         g_object_set_data( holder, "box3d_angle_y_action", eact );
2625         box3d_angle_y = eact;
2626     }
2628     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2629         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2630     } else {
2631         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2632     }
2634     /* VP Y state */
2635     {
2636         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2637                                                       // Translators: VP is short for 'vanishing point'
2638                                                       _("State of VP in Y direction"),
2639                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2640                                                       "toggle_vp_y",
2641                                                       Inkscape::ICON_SIZE_DECORATION );
2642         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2643         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2644         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2645         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2646         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2647     }
2649     /* Angle Z */
2650     {
2651         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2652         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2653         eact = create_adjustment_action( "3DBoxAngleZAction",
2654                                          _("Angle in Z direction"), _("Angle Z:"),
2655                                          // Translators: PL is short for 'perspective line'
2656                                          _("Angle of PLs in Z direction"),
2657                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2658                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2659                                          -360.0, 360.0, 1.0, 10.0,
2660                                          labels, values, G_N_ELEMENTS(labels),
2661                                          box3d_angle_z_value_changed );
2662         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2663         g_object_set_data( holder, "box3d_angle_z_action", eact );
2664         box3d_angle_z = eact;
2665     }
2667     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2668         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2669     } else {
2670         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2671     }
2673     /* VP Z state */
2674     {
2675         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2676                                                       // Translators: VP is short for 'vanishing point'
2677                                                       _("State of VP in Z direction"),
2678                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2679                                                       "toggle_vp_z",
2680                                                       Inkscape::ICON_SIZE_DECORATION );
2681         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2682         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2683         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2684         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2685         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2686     }
2688     sigc::connection *connection = new sigc::connection(
2689         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2690        );
2691     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2692     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2695 //########################
2696 //##       Spiral       ##
2697 //########################
2699 static void
2700 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2702     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2704     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2705         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2706     }
2708     // quit if run by the attr_changed listener
2709     if (g_object_get_data( tbl, "freeze" )) {
2710         return;
2711     }
2713     // in turn, prevent listener from responding
2714     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2716     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2718     bool modmade = false;
2719     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2720          items != NULL;
2721          items = items->next)
2722     {
2723         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2724             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2725             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2726             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2727             modmade = true;
2728         }
2729     }
2731     g_free(namespaced_name);
2733     if (modmade) {
2734         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2735                                    _("Change spiral"));
2736     }
2738     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2741 static void
2742 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2744     sp_spl_tb_value_changed(adj, tbl, "revolution");
2747 static void
2748 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2750     sp_spl_tb_value_changed(adj, tbl, "expansion");
2753 static void
2754 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2756     sp_spl_tb_value_changed(adj, tbl, "t0");
2759 static void
2760 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2762     GtkWidget *tbl = GTK_WIDGET(obj);
2764     GtkAdjustment *adj;
2766     // fixme: make settable
2767     gdouble rev = 5;
2768     gdouble exp = 1.0;
2769     gdouble t0 = 0.0;
2771     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2772     gtk_adjustment_set_value(adj, rev);
2773     gtk_adjustment_value_changed(adj);
2775     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2776     gtk_adjustment_set_value(adj, exp);
2777     gtk_adjustment_value_changed(adj);
2779     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2780     gtk_adjustment_set_value(adj, t0);
2781     gtk_adjustment_value_changed(adj);
2783     spinbutton_defocus(GTK_OBJECT(tbl));
2787 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2788                                          gchar const */*old_value*/, gchar const */*new_value*/,
2789                                          bool /*is_interactive*/, gpointer data)
2791     GtkWidget *tbl = GTK_WIDGET(data);
2793     // quit if run by the _changed callbacks
2794     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2795         return;
2796     }
2798     // in turn, prevent callbacks from responding
2799     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2801     GtkAdjustment *adj;
2802     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2803     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2805     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2806     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2808     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2809     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2811     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2815 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2816     NULL, /* child_added */
2817     NULL, /* child_removed */
2818     spiral_tb_event_attr_changed,
2819     NULL, /* content_changed */
2820     NULL  /* order_changed */
2821 };
2823 static void
2824 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2826     int n_selected = 0;
2827     Inkscape::XML::Node *repr = NULL;
2829     purge_repr_listener( tbl, tbl );
2831     for (GSList const *items = selection->itemList();
2832          items != NULL;
2833          items = items->next)
2834     {
2835         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2836             n_selected++;
2837             repr = SP_OBJECT_REPR((SPItem *) items->data);
2838         }
2839     }
2841     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2843     if (n_selected == 0) {
2844         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2845     } else if (n_selected == 1) {
2846         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2848         if (repr) {
2849             g_object_set_data( tbl, "repr", repr );
2850             Inkscape::GC::anchor(repr);
2851             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2852             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2853         }
2854     } else {
2855         // FIXME: implement averaging of all parameters for multiple selected
2856         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2857         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2858     }
2862 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2864     EgeAdjustmentAction* eact = 0;
2866     {
2867         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2868         ege_output_action_set_use_markup( act, TRUE );
2869         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2870         g_object_set_data( holder, "mode_action", act );
2871     }
2873     /* Revolution */
2874     {
2875         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2876         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2877         eact = create_adjustment_action( "SpiralRevolutionAction",
2878                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2879                                          "tools.shapes.spiral", "revolution", 3.0,
2880                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2881                                          0.01, 1024.0, 0.1, 1.0,
2882                                          labels, values, G_N_ELEMENTS(labels),
2883                                          sp_spl_tb_revolution_value_changed, 1, 2);
2884         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2885     }
2887     /* Expansion */
2888     {
2889         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2890         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2891         eact = create_adjustment_action( "SpiralExpansionAction",
2892                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2893                                          "tools.shapes.spiral", "expansion", 1.0,
2894                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2895                                          0.0, 1000.0, 0.01, 1.0,
2896                                          labels, values, G_N_ELEMENTS(labels),
2897                                          sp_spl_tb_expansion_value_changed);
2898         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2899     }
2901     /* T0 */
2902     {
2903         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2904         gdouble values[] = {0, 0.5, 0.9};
2905         eact = create_adjustment_action( "SpiralT0Action",
2906                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2907                                          "tools.shapes.spiral", "t0", 0.0,
2908                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2909                                          0.0, 0.999, 0.01, 1.0,
2910                                          labels, values, G_N_ELEMENTS(labels),
2911                                          sp_spl_tb_t0_value_changed);
2912         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2913     }
2915     /* Reset */
2916     {
2917         InkAction* inky = ink_action_new( "SpiralResetAction",
2918                                           _("Defaults"),
2919                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2920                                           GTK_STOCK_CLEAR,
2921                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2922         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2923         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2924     }
2927     sigc::connection *connection = new sigc::connection(
2928         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2929         );
2930     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2931     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2934 //########################
2935 //##     Pen/Pencil    ##
2936 //########################
2939 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2941     // Put stuff here
2944 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2946     // Put stuff here
2949 //########################
2950 //##       Tweak        ##
2951 //########################
2953 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2955     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2958 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2960     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2963 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2965     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2968 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2970     int mode = ege_select_one_action_get_active( act );
2971     prefs_set_int_attribute("tools.tweak", "mode", mode);
2973     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2974     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2975     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2976     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2977     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2978     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2979     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2980         if (doh) gtk_action_set_sensitive (doh, TRUE);
2981         if (dos) gtk_action_set_sensitive (dos, TRUE);
2982         if (dol) gtk_action_set_sensitive (dol, TRUE);
2983         if (doo) gtk_action_set_sensitive (doo, TRUE);
2984         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2985         if (fid) gtk_action_set_sensitive (fid, FALSE);
2986     } else {
2987         if (doh) gtk_action_set_sensitive (doh, FALSE);
2988         if (dos) gtk_action_set_sensitive (dos, FALSE);
2989         if (dol) gtk_action_set_sensitive (dol, FALSE);
2990         if (doo) gtk_action_set_sensitive (doo, FALSE);
2991         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2992         if (fid) gtk_action_set_sensitive (fid, TRUE);
2993     }
2996 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2998     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3001 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3002     bool show = gtk_toggle_action_get_active( act );
3003     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3005 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3006     bool show = gtk_toggle_action_get_active( act );
3007     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3009 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3010     bool show = gtk_toggle_action_get_active( act );
3011     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3013 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3014     bool show = gtk_toggle_action_get_active( act );
3015     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3018 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3020     {
3021         /* Width */
3022         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3023         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3024         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3025                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3026                                                               "tools.tweak", "width", 15,
3027                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3028                                                               1, 100, 1.0, 10.0,
3029                                                               labels, values, G_N_ELEMENTS(labels),
3030                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3031         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3032         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3033     }
3036     {
3037         /* Force */
3038         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3039         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3040         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3041                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3042                                                               "tools.tweak", "force", 20,
3043                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3044                                                               1, 100, 1.0, 10.0,
3045                                                               labels, values, G_N_ELEMENTS(labels),
3046                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3047         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3048         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3049     }
3051     /* Mode */
3052     {
3053         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3055         GtkTreeIter iter;
3056         gtk_list_store_append( model, &iter );
3057         gtk_list_store_set( model, &iter,
3058                             0, _("Push mode"),
3059                             1, _("Push parts of paths in any direction"),
3060                             2, "tweak_push_mode",
3061                             -1 );
3063         gtk_list_store_append( model, &iter );
3064         gtk_list_store_set( model, &iter,
3065                             0, _("Shrink mode"),
3066                             1, _("Shrink (inset) parts of paths"),
3067                             2, "tweak_shrink_mode",
3068                             -1 );
3070         gtk_list_store_append( model, &iter );
3071         gtk_list_store_set( model, &iter,
3072                             0, _("Grow mode"),
3073                             1, _("Grow (outset) parts of paths"),
3074                             2, "tweak_grow_mode",
3075                             -1 );
3077         gtk_list_store_append( model, &iter );
3078         gtk_list_store_set( model, &iter,
3079                             0, _("Attract mode"),
3080                             1, _("Attract parts of paths towards cursor"),
3081                             2, "tweak_attract_mode",
3082                             -1 );
3084         gtk_list_store_append( model, &iter );
3085         gtk_list_store_set( model, &iter,
3086                             0, _("Repel mode"),
3087                             1, _("Repel parts of paths from cursor"),
3088                             2, "tweak_repel_mode",
3089                             -1 );
3091         gtk_list_store_append( model, &iter );
3092         gtk_list_store_set( model, &iter,
3093                             0, _("Roughen mode"),
3094                             1, _("Roughen parts of paths"),
3095                             2, "tweak_roughen_mode",
3096                             -1 );
3098         gtk_list_store_append( model, &iter );
3099         gtk_list_store_set( model, &iter,
3100                             0, _("Color paint mode"),
3101                             1, _("Paint the tool's color upon selected objects"),
3102                             2, "tweak_colorpaint_mode",
3103                             -1 );
3105         gtk_list_store_append( model, &iter );
3106         gtk_list_store_set( model, &iter,
3107                             0, _("Color jitter mode"),
3108                             1, _("Jitter the colors of selected objects"),
3109                             2, "tweak_colorjitter_mode",
3110                             -1 );
3112         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3113         g_object_set( act, "short_label", _("Mode:"), NULL );
3114         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3115         g_object_set_data( holder, "mode_action", act );
3117         ege_select_one_action_set_appearance( act, "full" );
3118         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3119         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3120         ege_select_one_action_set_icon_column( act, 2 );
3121         ege_select_one_action_set_tooltip_column( act, 1  );
3123         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3124         ege_select_one_action_set_active( act, mode );
3125         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3127         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3128     }
3130     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3132     {
3133         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3134         ege_output_action_set_use_markup( act, TRUE );
3135         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3136         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3137             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3138         g_object_set_data( holder, "tweak_channels_label", act);
3139     }
3141     {
3142         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3143                                                       _("Hue"),
3144                                                       _("In color mode, act on objects' hue"),
3145                                                       NULL,
3146                                                       Inkscape::ICON_SIZE_DECORATION );
3147         g_object_set( act, "short_label", _("H"), NULL );
3148         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3149         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3150         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3151         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3152             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3153         g_object_set_data( holder, "tweak_doh", act);
3154     }
3155     {
3156         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3157                                                       _("Saturation"),
3158                                                       _("In color mode, act on objects' saturation"),
3159                                                       NULL,
3160                                                       Inkscape::ICON_SIZE_DECORATION );
3161         g_object_set( act, "short_label", _("S"), NULL );
3162         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3163         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3164         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3165         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3166             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3167         g_object_set_data( holder, "tweak_dos", act );
3168     }
3169     {
3170         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3171                                                       _("Lightness"),
3172                                                       _("In color mode, act on objects' lightness"),
3173                                                       NULL,
3174                                                       Inkscape::ICON_SIZE_DECORATION );
3175         g_object_set( act, "short_label", _("L"), NULL );
3176         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3177         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3178         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3179         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3180             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3181         g_object_set_data( holder, "tweak_dol", act );
3182     }
3183     {
3184         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3185                                                       _("Opacity"),
3186                                                       _("In color mode, act on objects' opacity"),
3187                                                       NULL,
3188                                                       Inkscape::ICON_SIZE_DECORATION );
3189         g_object_set( act, "short_label", _("O"), NULL );
3190         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3191         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3192         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3193         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3194             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3195         g_object_set_data( holder, "tweak_doo", act );
3196     }
3198     {   /* Fidelity */
3199         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3200         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3201         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3202                                                               _("Fidelity"), _("Fidelity:"),
3203                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3204                                                               "tools.tweak", "fidelity", 50,
3205                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3206                                                               1, 100, 1.0, 10.0,
3207                                                               labels, values, G_N_ELEMENTS(labels),
3208                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3209         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3210         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3211         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3212             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3213         g_object_set_data( holder, "tweak_fidelity", eact );
3214     }
3217     /* Use Pressure button */
3218     {
3219         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3220                                                       _("Pressure"),
3221                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3222                                                       "use_pressure",
3223                                                       Inkscape::ICON_SIZE_DECORATION );
3224         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3225         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3226         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3227     }
3232 //########################
3233 //##     Calligraphy    ##
3234 //########################
3236 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3238     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3241 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3243     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3246 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3248     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3251 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3253     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3256 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3258     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3261 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3263     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3266 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3268     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3271 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3273     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3276 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3278     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3281 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3283     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3286 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3288     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3290     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3293 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3295     // FIXME: make defaults settable via Inkscape Options
3296     struct KeyValue {
3297         char const *key;
3298         double value;
3299     } const key_values[] = {
3300         {"mass", 0.02},
3301         {"wiggle", 0.0},
3302         {"angle", 30.0},
3303         {"width", 15},
3304         {"thinning", 0.1},
3305         {"tremor", 0.0},
3306         {"flatness", 0.9},
3307         {"cap_rounding", 0.0}
3308     };
3310     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3311         KeyValue const &kv = key_values[i];
3312         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3313         if ( adj ) {
3314             gtk_adjustment_set_value(adj, kv.value);
3315         }
3316     }
3320 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3322     {
3323         EgeAdjustmentAction* calligraphy_angle = 0;
3325         {
3326         /* Width */
3327         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3328         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3329         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3330                                                               _("Pen Width"), _("Width:"),
3331                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3332                                                               "tools.calligraphic", "width", 15,
3333                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3334                                                               1, 100, 1.0, 10.0,
3335                                                               labels, values, G_N_ELEMENTS(labels),
3336                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3337         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3338         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3339         }
3341         {
3342         /* Thinning */
3343             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3344             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3345         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3346                                                               _("Stroke Thinning"), _("Thinning:"),
3347                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3348                                                               "tools.calligraphic", "thinning", 0.1,
3349                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3350                                                               -1.0, 1.0, 0.01, 0.1,
3351                                                               labels, values, G_N_ELEMENTS(labels),
3352                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3353         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3354         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3355         }
3357         {
3358         /* Angle */
3359         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3360         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3361         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3362                                                               _("Pen Angle"), _("Angle:"),
3363                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3364                                                               "tools.calligraphic", "angle", 30,
3365                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3366                                                               -90.0, 90.0, 1.0, 10.0,
3367                                                               labels, values, G_N_ELEMENTS(labels),
3368                                                               sp_ddc_angle_value_changed, 1, 0 );
3369         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3370         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3371         calligraphy_angle = eact;
3372         }
3374         {
3375         /* Fixation */
3376             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3377         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3378         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3379                                                               _("Fixation"), _("Fixation:"),
3380                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3381                                                               "tools.calligraphic", "flatness", 0.9,
3382                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3383                                                               0.0, 1.0, 0.01, 0.1,
3384                                                               labels, values, G_N_ELEMENTS(labels),
3385                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3386         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3387         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3388         }
3390         {
3391         /* Cap Rounding */
3392             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3393         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3394         // TRANSLATORS: "cap" means "end" (both start and finish) here
3395         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3396                                                               _("Cap rounding"), _("Caps:"),
3397                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3398                                                               "tools.calligraphic", "cap_rounding", 0.0,
3399                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3400                                                               0.0, 5.0, 0.01, 0.1,
3401                                                               labels, values, G_N_ELEMENTS(labels),
3402                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3403         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3404         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3405         }
3407         {
3408         /* Tremor */
3409             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3410         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3411         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3412                                                               _("Stroke Tremor"), _("Tremor:"),
3413                                                               _("Increase to make strokes rugged and trembling"),
3414                                                               "tools.calligraphic", "tremor", 0.0,
3415                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3416                                                               0.0, 1.0, 0.01, 0.1,
3417                                                               labels, values, G_N_ELEMENTS(labels),
3418                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3420         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3421         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3422         }
3424         {
3425         /* Wiggle */
3426         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3427         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3428         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3429                                                               _("Pen Wiggle"), _("Wiggle:"),
3430                                                               _("Increase to make the pen waver and wiggle"),
3431                                                               "tools.calligraphic", "wiggle", 0.0,
3432                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3433                                                               0.0, 1.0, 0.01, 0.1,
3434                                                               labels, values, G_N_ELEMENTS(labels),
3435                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3436         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3437         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3438         }
3440         {
3441         /* Mass */
3442             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3443         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3444         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3445                                                               _("Pen Mass"), _("Mass:"),
3446                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3447                                                               "tools.calligraphic", "mass", 0.02,
3448                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3449                                                               0.0, 1.0, 0.01, 0.1,
3450                                                               labels, values, G_N_ELEMENTS(labels),
3451                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3452         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3453         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3454         }
3457         /* Trace Background button */
3458         {
3459             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3460                                                           _("Trace Background"),
3461                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3462                                                           "trace_background",
3463                                                           Inkscape::ICON_SIZE_DECORATION );
3464             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3465             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3466             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3467         }
3469         /* Use Pressure button */
3470         {
3471             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3472                                                           _("Pressure"),
3473                                                           _("Use the pressure of the input device to alter the width of the pen"),
3474                                                           "use_pressure",
3475                                                           Inkscape::ICON_SIZE_DECORATION );
3476             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3477             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3478             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3479         }
3481         /* Use Tilt button */
3482         {
3483             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3484                                                           _("Tilt"),
3485                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3486                                                           "use_tilt",
3487                                                           Inkscape::ICON_SIZE_DECORATION );
3488             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3489             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3490             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3491             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3492         }
3494         /* Reset */
3495         {
3496             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3497                                              _("Defaults"),
3498                                              _("Reset all parameters to defaults"),
3499                                              GTK_STOCK_CLEAR );
3500             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3501             gtk_action_group_add_action( mainActions, act );
3502             gtk_action_set_sensitive( act, TRUE );
3503         }
3504     }
3508 //########################
3509 //##    Circle / Arc    ##
3510 //########################
3512 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3514     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3515     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3517     if (v1 == 0 && v2 == 0) {
3518         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3519             gtk_action_set_sensitive( ocb, FALSE );
3520             gtk_action_set_sensitive( make_whole, FALSE );
3521         }
3522     } else {
3523         gtk_action_set_sensitive( ocb, TRUE );
3524         gtk_action_set_sensitive( make_whole, TRUE );
3525     }
3528 static void
3529 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3531     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3533     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3534         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3535     }
3537     // quit if run by the attr_changed listener
3538     if (g_object_get_data( tbl, "freeze" )) {
3539         return;
3540     }
3542     // in turn, prevent listener from responding
3543     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3545     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3547     bool modmade = false;
3548     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3549          items != NULL;
3550          items = items->next)
3551     {
3552         SPItem *item = SP_ITEM(items->data);
3554         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3556             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3557             SPArc *arc = SP_ARC(item);
3559             if (!strcmp(value_name, "start"))
3560                 ge->start = (adj->value * M_PI)/ 180;
3561             else
3562                 ge->end = (adj->value * M_PI)/ 180;
3564             sp_genericellipse_normalize(ge);
3565             ((SPObject *)arc)->updateRepr();
3566             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3568             modmade = true;
3569         }
3570     }
3572     g_free(namespaced_name);
3574     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3576     sp_arctb_sensitivize( tbl, adj->value, other->value );
3578     if (modmade) {
3579         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3580                                    _("Arc: Change start/end"));
3581     }
3583     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3587 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3589     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3592 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3594     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3597 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3599     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3600     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3601         if ( ege_select_one_action_get_active( act ) != 0 ) {
3602             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3603         } else {
3604             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3605         }
3606     }
3608     // quit if run by the attr_changed listener
3609     if (g_object_get_data( tbl, "freeze" )) {
3610         return;
3611     }
3613     // in turn, prevent listener from responding
3614     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3616     bool modmade = false;
3618     if ( ege_select_one_action_get_active(act) != 0 ) {
3619         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3620              items != NULL;
3621              items = items->next)
3622         {
3623             if (SP_IS_ARC((SPItem *) items->data)) {
3624                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3625                 repr->setAttribute("sodipodi:open", "true");
3626                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3627                 modmade = true;
3628             }
3629         }
3630     } else {
3631         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3632              items != NULL;
3633              items = items->next)
3634         {
3635             if (SP_IS_ARC((SPItem *) items->data))    {
3636                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3637                 repr->setAttribute("sodipodi:open", NULL);
3638                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3639                 modmade = true;
3640             }
3641         }
3642     }
3644     if (modmade) {
3645         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3646                                    _("Arc: Change open/closed"));
3647     }
3649     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3652 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3654     GtkAdjustment *adj;
3655     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3656     gtk_adjustment_set_value(adj, 0.0);
3657     gtk_adjustment_value_changed(adj);
3659     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3660     gtk_adjustment_set_value(adj, 0.0);
3661     gtk_adjustment_value_changed(adj);
3663     spinbutton_defocus( GTK_OBJECT(obj) );
3666 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3667                                       gchar const */*old_value*/, gchar const */*new_value*/,
3668                                       bool /*is_interactive*/, gpointer data)
3670     GObject *tbl = G_OBJECT(data);
3672     // quit if run by the _changed callbacks
3673     if (g_object_get_data( tbl, "freeze" )) {
3674         return;
3675     }
3677     // in turn, prevent callbacks from responding
3678     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3680     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3681     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3683     GtkAdjustment *adj1,*adj2;
3684     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3685     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3686     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3687     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3689     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3691     char const *openstr = NULL;
3692     openstr = repr->attribute("sodipodi:open");
3693     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3695     if (openstr) {
3696         ege_select_one_action_set_active( ocb, 1 );
3697     } else {
3698         ege_select_one_action_set_active( ocb, 0 );
3699     }
3701     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3704 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3705     NULL, /* child_added */
3706     NULL, /* child_removed */
3707     arc_tb_event_attr_changed,
3708     NULL, /* content_changed */
3709     NULL  /* order_changed */
3710 };
3713 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3715     int n_selected = 0;
3716     Inkscape::XML::Node *repr = NULL;
3718     purge_repr_listener( tbl, tbl );
3720     for (GSList const *items = selection->itemList();
3721          items != NULL;
3722          items = items->next)
3723     {
3724         if (SP_IS_ARC((SPItem *) items->data)) {
3725             n_selected++;
3726             repr = SP_OBJECT_REPR((SPItem *) items->data);
3727         }
3728     }
3730     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3732     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3733     if (n_selected == 0) {
3734         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3735     } else if (n_selected == 1) {
3736         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3737         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3739         if (repr) {
3740             g_object_set_data( tbl, "repr", repr );
3741             Inkscape::GC::anchor(repr);
3742             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3743             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3744         }
3745     } else {
3746         // FIXME: implement averaging of all parameters for multiple selected
3747         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3748         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3749         sp_arctb_sensitivize( tbl, 1, 0 );
3750     }
3754 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3756     EgeAdjustmentAction* eact = 0;
3759     {
3760         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3761         ege_output_action_set_use_markup( act, TRUE );
3762         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3763         g_object_set_data( holder, "mode_action", act );
3764     }
3766     /* Start */
3767     {
3768         eact = create_adjustment_action( "ArcStartAction",
3769                                          _("Start"), _("Start:"),
3770                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3771                                          "tools.shapes.arc", "start", 0.0,
3772                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3773                                          -360.0, 360.0, 1.0, 10.0,
3774                                          0, 0, 0,
3775                                          sp_arctb_start_value_changed);
3776         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3777     }
3779     /* End */
3780     {
3781         eact = create_adjustment_action( "ArcEndAction",
3782                                          _("End"), _("End:"),
3783                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3784                                          "tools.shapes.arc", "end", 0.0,
3785                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3786                                          -360.0, 360.0, 1.0, 10.0,
3787                                          0, 0, 0,
3788                                          sp_arctb_end_value_changed);
3789         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3790     }
3792     /* Segments / Pie checkbox */
3793     {
3794         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3796         GtkTreeIter iter;
3797         gtk_list_store_append( model, &iter );
3798         gtk_list_store_set( model, &iter,
3799                             0, _("Closed arc"),
3800                             1, _("Switch to segment (closed shape with two radii)"),
3801                             2, "circle_closed_arc",
3802                             -1 );
3804         gtk_list_store_append( model, &iter );
3805         gtk_list_store_set( model, &iter,
3806                             0, _("Open Arc"),
3807                             1, _("Switch to arc (unclosed shape)"),
3808                             2, "circle_open_arc",
3809                             -1 );
3811         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3812         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3813         g_object_set_data( holder, "open_action", act );
3815         ege_select_one_action_set_appearance( act, "full" );
3816         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3817         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3818         ege_select_one_action_set_icon_column( act, 2 );
3819         ege_select_one_action_set_tooltip_column( act, 1  );
3821         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3822         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3823         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3824         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3825     }
3827     /* Make Whole */
3828     {
3829         InkAction* inky = ink_action_new( "ArcResetAction",
3830                                           _("Make whole"),
3831                                           _("Make the shape a whole ellipse, not arc or segment"),
3832                                           "reset_circle",
3833                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3834         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3835         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3836         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3837         g_object_set_data( holder, "make_whole", inky );
3838     }
3840     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3841     // sensitivize make whole and open checkbox
3842     {
3843         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3844         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3845         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3846     }
3849     sigc::connection *connection = new sigc::connection(
3850         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3851         );
3852     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3853     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3859 // toggle button callbacks and updaters
3861 //########################
3862 //##      Dropper       ##
3863 //########################
3865 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3866     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3867     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3868     if ( set_action ) {
3869         if ( gtk_toggle_action_get_active( act ) ) {
3870             gtk_action_set_sensitive( set_action, TRUE );
3871         } else {
3872             gtk_action_set_sensitive( set_action, FALSE );
3873         }
3874     }
3876     spinbutton_defocus(GTK_OBJECT(tbl));
3879 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3880     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3881     spinbutton_defocus(GTK_OBJECT(tbl));
3885 /**
3886  * Dropper auxiliary toolbar construction and setup.
3887  *
3888  * TODO: Would like to add swatch of current color.
3889  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3890  *       can drag and drop places. Will provide a nice mixing palette.
3891  */
3892 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3894     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3896     {
3897         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3898                                                       _("Pick alpha"),
3899                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3900                                                       "color_alpha_get",
3901                                                       Inkscape::ICON_SIZE_DECORATION );
3902         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3903         g_object_set_data( holder, "pick_action", act );
3904         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3905         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3906     }
3908     {
3909         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3910                                                       _("Set alpha"),
3911                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3912                                                       "color_alpha_set",
3913                                                       Inkscape::ICON_SIZE_DECORATION );
3914         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3915         g_object_set_data( holder, "set_action", act );
3916         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3917         // make sure it's disabled if we're not picking alpha
3918         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3919         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3920     }
3924 //########################
3925 //##    Text Toolbox    ##
3926 //########################
3927 /*
3928 static void
3929 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3931     //Call back for letter sizing spinbutton
3934 static void
3935 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3937     //Call back for line height spinbutton
3940 static void
3941 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3943     //Call back for horizontal kerning spinbutton
3946 static void
3947 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3949     //Call back for vertical kerning spinbutton
3952 static void
3953 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3955     //Call back for letter rotation spinbutton
3956 }*/
3958 namespace {
3960 bool visible = false;
3962 void
3963 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3965     SPStyle *query =
3966         sp_style_new (SP_ACTIVE_DOCUMENT);
3967     
3968     int result_fontspec =
3969         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3971     int result_family =
3972         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3974     int result_style =
3975         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3977     int result_numbers =
3978         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3980     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3982     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3983     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3984     {
3985         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3987         if (repr)
3988         {
3989             sp_style_read_from_repr (query, repr);
3990         }
3991         else
3992         {
3993             return;
3994         }
3995     }
3997     if (query->text)
3998     {
3999         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4000             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4001             gtk_entry_set_text (GTK_ENTRY (entry), "");
4003         } else if (query->text->font_specification.value || query->text->font_family.value) {
4005             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4006             
4007             // Get the font that corresponds 
4008             Glib::ustring familyName;
4009             
4010             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4011             if (font) {
4012                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4013                 font->Unref();
4014                 font = NULL;
4015             }
4016             
4017             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4019             Gtk::TreePath path;
4020             try {
4021                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4022             } catch (...) {
4023                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4024                 return;
4025             }
4027             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4028             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4030             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4032             gtk_tree_selection_select_path (tselection, path.gobj());
4033             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4035             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4036         }
4038         //Size
4039         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4040         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4041         g_object_set_data (tbl, "size-block", gpointer(1));
4042         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4043         g_object_set_data (tbl, "size-block", gpointer(0));
4044         free (str);
4046         //Anchor
4047         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4048         {
4049             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4050             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4051             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4052             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4053         }
4054         else
4055         {
4056             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4057             {
4058                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4059                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4060                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4061                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4062             }
4063             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4064             {
4065                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4066                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4067                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4068                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4069             }
4070             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4071             {
4072                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4073                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4074                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4075                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4076             }
4077         }
4079         //Style
4080         {
4081             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4083             gboolean active = gtk_toggle_button_get_active (button);
4084             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4086             if (active != check)
4087             {
4088                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4089                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4090                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4091             }
4092         }
4094         {
4095             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4097             gboolean active = gtk_toggle_button_get_active (button);
4098             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4100             if (active != check)
4101             {
4102                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4103                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4104                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4105             }
4106         }
4108         //Orientation
4109         //locking both buttons, changing one affect all group (both)
4110         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4111         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4113         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4114         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4116         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4117         {
4118             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4119         }
4120         else
4121         {
4122             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4123         }
4124         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4125         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4126     }
4128     sp_style_unref(query);
4131 void
4132 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4134     sp_text_toolbox_selection_changed (selection, tbl);
4137 void
4138 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4140     sp_text_toolbox_selection_changed (NULL, tbl);
4143 void
4144 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4145                                 GObject             *tbl)
4147     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4148     GtkTreeModel *model = 0;
4149     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4150     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4151     GtkTreeIter   iter;
4152     char         *family = 0;
4154     (void)popdown;
4156     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4157     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4159     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4160         return;
4161     }
4163     gtk_tree_model_get (model, &iter, 0, &family, -1);
4165     if (g_object_get_data (G_OBJECT (selection), "block"))
4166     {
4167         gtk_entry_set_text (GTK_ENTRY (entry), family);
4168         return;
4169     }
4171     gtk_entry_set_text (GTK_ENTRY (entry), family);
4173     SPStyle *query =
4174         sp_style_new (SP_ACTIVE_DOCUMENT);
4176     int result_fontspec =
4177         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4178     
4179     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4180     
4181     SPCSSAttr *css = sp_repr_css_attr_new ();
4182     
4183     
4184     // First try to get the font spec from the stored value
4185     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4186     
4187     if (fontSpec.empty()) {
4188         // Construct a new font specification if it does not yet exist
4189         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4190         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4191         fontFromStyle->Unref();
4192     }
4193     
4194     if (!fontSpec.empty()) {
4195         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4196         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4197             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4198             if (font) {
4199                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4200                 
4201                 // Set all the these just in case they were altered when finding the best
4202                 // match for the new family and old style...
4203                 
4204                 gchar c[256];
4205                 
4206                 font->Family(c, 256);
4207                 sp_repr_css_set_property (css, "font-family", c);
4208                 
4209                 font->Attribute( "weight", c, 256);
4210                 sp_repr_css_set_property (css, "font-weight", c);
4211                 
4212                 font->Attribute("style", c, 256);
4213                 sp_repr_css_set_property (css, "font-style", c);
4214                 
4215                 font->Attribute("stretch", c, 256);
4216                 sp_repr_css_set_property (css, "font-stretch", c);
4217                 
4218                 font->Attribute("variant", c, 256);
4219                 sp_repr_css_set_property (css, "font-variant", c);
4220                 
4221                 font->Unref();
4222             }
4223         }
4224     }
4226     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4227     if (result_fontspec == QUERY_STYLE_NOTHING)
4228     {
4229         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4230         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4231     }
4232     else
4233     {
4234         sp_desktop_set_style (desktop, css, true, true);
4235     }
4237     sp_style_unref(query);
4239     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4240                                    _("Text: Change font family"));
4241     sp_repr_css_attr_unref (css);
4242     free (family);
4243     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4245     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4248 void
4249 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4250                                        GObject      *tbl)
4252     const char *family = gtk_entry_get_text (entry);
4254     try {
4255         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4256         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4257         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4258         gtk_tree_selection_select_path (selection, path.gobj());
4259         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4260         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4261     } catch (...) {
4262         if (family && strlen (family))
4263         {
4264             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4265         }
4266     }
4269 void
4270 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4271                                    gpointer          data)
4273     if (g_object_get_data (G_OBJECT (button), "block")) return;
4274     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4275     int prop = GPOINTER_TO_INT(data);
4277     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4278     SPCSSAttr *css = sp_repr_css_attr_new ();
4280     switch (prop)
4281     {
4282         case 0:
4283         {
4284             sp_repr_css_set_property (css, "text-anchor", "start");
4285             sp_repr_css_set_property (css, "text-align", "start");
4286             break;
4287         }
4288         case 1:
4289         {
4290             sp_repr_css_set_property (css, "text-anchor", "middle");
4291             sp_repr_css_set_property (css, "text-align", "center");
4292             break;
4293         }
4295         case 2:
4296         {
4297             sp_repr_css_set_property (css, "text-anchor", "end");
4298             sp_repr_css_set_property (css, "text-align", "end");
4299             break;
4300         }
4302         case 3:
4303         {
4304             sp_repr_css_set_property (css, "text-anchor", "start");
4305             sp_repr_css_set_property (css, "text-align", "justify");
4306             break;
4307         }
4308     }
4310     SPStyle *query =
4311         sp_style_new (SP_ACTIVE_DOCUMENT);
4312     int result_numbers =
4313         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4315     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4316     if (result_numbers == QUERY_STYLE_NOTHING)
4317     {
4318         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4319     }
4321     sp_style_unref(query);
4323     sp_desktop_set_style (desktop, css, true, true);
4324     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4325                                    _("Text: Change alignment"));
4326     sp_repr_css_attr_unref (css);
4328     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4331 void
4332 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4333                                gpointer          data)
4335     if (g_object_get_data (G_OBJECT (button), "block")) return;
4337     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4338     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4339     int          prop       = GPOINTER_TO_INT(data);
4340     bool         active     = gtk_toggle_button_get_active (button);
4342     SPStyle *query =
4343         sp_style_new (SP_ACTIVE_DOCUMENT);
4344     
4345     int result_fontspec =
4346         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4348     int result_family =
4349         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4351     int result_style =
4352         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4354     int result_numbers =
4355         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4356     
4357     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4358     Glib::ustring newFontSpec = "";
4359     
4360     if (fontSpec.empty()) {
4361         // Construct a new font specification if it does not yet exist
4362         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4363         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4364         fontFromStyle->Unref();
4365     }
4366     
4367     switch (prop)
4368     {
4369         case 0:
4370         {
4371             if (!fontSpec.empty()) {
4372                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4373             }
4374             if (fontSpec != newFontSpec) {
4375                 // Don't even set the bold if the font didn't exist on the system
4376                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4377             }
4378             break;
4379         }
4381         case 1:
4382         {
4383             if (!fontSpec.empty()) {
4384                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4385             }
4386             if (fontSpec != newFontSpec) {
4387                 // Don't even set the italic if the font didn't exist on the system
4388                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4389             }
4390             break;
4391         }
4392     }
4394     if (!newFontSpec.empty()) {
4395         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str()); 
4396     }
4398     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4399     if (result_fontspec == QUERY_STYLE_NOTHING)
4400     {
4401         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4402     }
4404     sp_style_unref(query);
4406     sp_desktop_set_style (desktop, css, true, true);
4407     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4408                                    _("Text: Change font style"));
4409     sp_repr_css_attr_unref (css);
4411     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4414 void
4415 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4416                                      gpointer         data)
4418     if (g_object_get_data (G_OBJECT (button), "block")) {
4419         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4420         return;
4421     }
4423     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4424     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4425     int          prop       = GPOINTER_TO_INT(data);
4427     switch (prop)
4428     {
4429         case 0:
4430         {
4431             sp_repr_css_set_property (css, "writing-mode", "lr");
4432             break;
4433         }
4435         case 1:
4436         {
4437             sp_repr_css_set_property (css, "writing-mode", "tb");
4438             break;
4439         }
4440     }
4442     SPStyle *query =
4443         sp_style_new (SP_ACTIVE_DOCUMENT);
4444     int result_numbers =
4445         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4447     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4448     if (result_numbers == QUERY_STYLE_NOTHING)
4449     {
4450         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4451     }
4453     sp_desktop_set_style (desktop, css, true, true);
4454     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4455                                    _("Text: Change orientation"));
4456     sp_repr_css_attr_unref (css);
4458     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4461 gboolean
4462 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4464     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4465     if (!desktop) return FALSE;
4467     switch (get_group0_keyval (event)) {
4468         case GDK_Escape: // defocus
4469             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4470             sp_text_toolbox_selection_changed (NULL, tbl); // update
4471             return TRUE; // I consumed the event
4472             break;
4473     }
4474     return FALSE;
4477 gboolean
4478 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4480     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4481     if (!desktop) return FALSE;
4483     switch (get_group0_keyval (event)) {
4484         case GDK_KP_Enter:
4485         case GDK_Return:
4486         case GDK_Escape: // defocus
4487             gtk_widget_hide (w);
4488             visible = false;
4489             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4490             return TRUE; // I consumed the event
4491             break;
4492     }
4493     return FALSE;
4497 void
4498 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4499                                GObject     *tbl)
4501     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4503     if (g_object_get_data (tbl, "size-block")) return;
4505     // If this is not from selecting a size in the list (in which case get_active will give the
4506     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4507     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4508     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4509     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4510         return;
4512     gchar *endptr;
4513     gdouble value = -1;
4514     char *text = gtk_combo_box_get_active_text (cbox);
4515     if (text) {
4516         value = g_strtod (text, &endptr);
4517         if (endptr == text) // conversion failed, non-numeric input
4518             value = -1;
4519         free (text);
4520     }
4521     if (value <= 0) {
4522         return; // could not parse value 
4523     }
4525     SPCSSAttr *css = sp_repr_css_attr_new ();
4526     Inkscape::CSSOStringStream osfs;
4527     osfs << value;
4528     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4530     SPStyle *query =
4531         sp_style_new (SP_ACTIVE_DOCUMENT);
4532     int result_numbers =
4533         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4535     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4536     if (result_numbers == QUERY_STYLE_NOTHING)
4537     {
4538         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4539     }
4541     sp_style_unref(query);
4543     sp_desktop_set_style (desktop, css, true, true);
4544     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4545                                    _("Text: Change font size"));
4546     sp_repr_css_attr_unref (css);
4548     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4551 gboolean
4552 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus *event, GObject *tbl)
4554     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4555     if (!desktop) return FALSE;
4557     if (!g_object_get_data (tbl, "esc-pressed")) {
4558         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4559         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4560         sp_text_toolbox_size_changed (cbox, tbl);
4561         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4562     }
4563     return FALSE; // I consumed the event
4567 gboolean
4568 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4570     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4571     if (!desktop) return FALSE;
4573     switch (get_group0_keyval (event)) {
4574         case GDK_Escape: // defocus
4575             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4576             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4577             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4578             return TRUE; // I consumed the event
4579             break;
4580         case GDK_Return: // defocus
4581         case GDK_KP_Enter:
4582             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4583             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4584             sp_text_toolbox_size_changed (cbox, tbl);
4585             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4586             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4587             return TRUE; // I consumed the event
4588             break;
4589     }
4590     return FALSE;
4593 void
4594 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4595                                          GObject            *tbl)
4597     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4598     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4599     int x, y;
4601     if (!visible)
4602     {
4603         gdk_window_get_origin (widget->window, &x, &y);
4604         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4605         gtk_widget_show_all (popdown);
4607         gdk_pointer_grab (widget->window, TRUE,
4608                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4609                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4610                                         GDK_POINTER_MOTION_MASK),
4611                           NULL, NULL, GDK_CURRENT_TIME);
4613         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4615         visible = true;
4616     }
4617     else
4618     {
4619         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4620         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4621         gtk_widget_hide (popdown);
4622         visible = false;
4623     }
4626 gboolean
4627 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4628                                  GdkEventFocus    */*event*/,
4629                                  GObject          */*tbl*/)
4631     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4632     return FALSE;
4635 gboolean
4636 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4637                                    GdkEventFocus    */*event*/,
4638                                    GObject          */*tbl*/)
4640     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4642     gtk_widget_hide (popdown);
4643     visible = false;
4644     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4645     return TRUE;
4648 void
4649 cell_data_func  (GtkTreeViewColumn */*column*/,
4650                  GtkCellRenderer   *cell,
4651                  GtkTreeModel      *tree_model,
4652                  GtkTreeIter       *iter,
4653                  gpointer           /*data*/)
4655     char        *family,
4656         *family_escaped,
4657         *sample_escaped;
4659     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4661     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4663     family_escaped = g_markup_escape_text (family, -1);
4664     sample_escaped = g_markup_escape_text (sample, -1);
4666     std::stringstream markup;
4667     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4668     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4670     free (family);
4671     free (family_escaped);
4672     free (sample_escaped);
4675 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4676     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4677     if (completion) {
4678         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4679         g_object_unref (completion);
4680     }
4683 GtkWidget*
4684 sp_text_toolbox_new (SPDesktop *desktop)
4686     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4688     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4689     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4691     GtkTooltips *tt = gtk_tooltips_new();
4692     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4694     ////////////Family
4695     //Window
4696     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4697     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4699     //Entry
4700     GtkWidget           *entry = gtk_entry_new ();
4701     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4702     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4703     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4704     gtk_entry_completion_set_text_column (completion, 0);
4705     gtk_entry_completion_set_minimum_key_length (completion, 1);
4706     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4707     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4708     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4709     aux_toolbox_space (tbl, 1);
4710     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4711     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4713     //Button
4714     GtkWidget   *button = gtk_button_new ();
4715     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4716     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4718     //Popdown
4719     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4720     GtkWidget           *treeview = gtk_tree_view_new ();
4722     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4723     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4724     gtk_tree_view_column_pack_start (column, cell, FALSE);
4725     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4726     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4727     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4729     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4730     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4731     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4733     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4735     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4736     gtk_container_add (GTK_CONTAINER (sw), treeview);
4738     gtk_container_add (GTK_CONTAINER (window), sw);
4739     gtk_widget_set_size_request (window, 300, 450);
4741     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4742     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4743     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4745     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4747     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4748     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4750     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4751     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4753     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4754     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4755     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4756     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4757     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4759     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4760     aux_toolbox_space (tbl, 1);
4761     GtkWidget *box = gtk_event_box_new ();
4762     gtk_container_add (GTK_CONTAINER (box), image);
4763     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4764     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4765     GtkTooltips *tooltips = gtk_tooltips_new ();
4766     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4767     gtk_widget_hide (GTK_WIDGET (box));
4768     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4770     ////////////Size
4771     const char *sizes[] = {
4772         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4773         "16", "18", "20", "22", "24", "28",
4774         "32", "36", "40", "48", "56", "64", "72", "144"
4775     };
4777     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4778     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4779     gtk_widget_set_size_request (cbox, 80, -1);
4780     aux_toolbox_space (tbl, 1);
4781     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4782     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4783     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4784     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4785     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4787     //spacer
4788     aux_toolbox_space (tbl, 4);
4789     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4791     ////////////Text anchor
4792     GtkWidget *group   = gtk_radio_button_new (NULL);
4793     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4794     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4796     // left
4797     GtkWidget *rbutton = group;
4798     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4799     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4800     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4802     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4803     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4804     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4805     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4807     // center
4808     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4809     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4810     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4811     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4813     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4814     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4815     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4816     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4818     // right
4819     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4820     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4821     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4822     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4824     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4825     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4826     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4827     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4829     // fill
4830     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4831     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4832     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4833     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4835     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4836     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4837     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4838     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4840     aux_toolbox_space (tbl, 1);
4841     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4843     //spacer
4844     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4846     ////////////Text style
4847     row = gtk_hbox_new (FALSE, 4);
4849     // bold
4850     rbutton = gtk_toggle_button_new ();
4851     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4852     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4853     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4854     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4856     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4857     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4858     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4860     // italic
4861     rbutton = gtk_toggle_button_new ();
4862     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4863     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4864     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4865     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4867     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4868     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4869     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4871     aux_toolbox_space (tbl, 1);
4872     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4874     //spacer
4875     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4877     ////////////Text orientation
4878     group   = gtk_radio_button_new (NULL);
4879     row     = gtk_hbox_new (FALSE, 4);
4880     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4882     // horizontal
4883     rbutton = group;
4884     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4885     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4886     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4887     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4889     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4890     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4891     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4893     // vertical
4894     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4895     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4896     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4897     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4898     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4900     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4901     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4902     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4903     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4906     //watch selection
4907     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4909     sigc::connection *c_selection_changed =
4910         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4911                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4912     pool->add_connection ("selection-changed", c_selection_changed);
4914     sigc::connection *c_selection_modified =
4915         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4916                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4917     pool->add_connection ("selection-modified", c_selection_modified);
4919     sigc::connection *c_subselection_changed =
4920         new sigc::connection (desktop->connectToolSubselectionChanged
4921                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4922     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4924     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4927     gtk_widget_show_all (tbl);
4928     return tbl;
4930 } // end of sp_text_toolbox_new()
4932 }//<unnamed> namespace
4935 //#########################
4936 //##      Connector      ##
4937 //#########################
4939 static void sp_connector_path_set_avoid(void)
4941     cc_selection_set_avoid(true);
4945 static void sp_connector_path_set_ignore(void)
4947     cc_selection_set_avoid(false);
4952 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4954     // quit if run by the _changed callbacks
4955     if (g_object_get_data( tbl, "freeze" )) {
4956         return;
4957     }
4959     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4960     SPDocument *doc = sp_desktop_document(desktop);
4962     if (!sp_document_get_undo_sensitive(doc))
4963     {
4964         return;
4965     }
4967     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4969     if ( repr->attribute("inkscape:connector-spacing") ) {
4970         gdouble priorValue = gtk_adjustment_get_value(adj);
4971         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4972         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4973             return;
4974         }
4975     } else if ( adj->value == defaultConnSpacing ) {
4976         return;
4977     }
4979     // in turn, prevent callbacks from responding
4980     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4982     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4983     SP_OBJECT(desktop->namedview)->updateRepr();
4985     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4986     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4987         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4988         NR::Matrix m = NR::identity();
4989         avoid_item_move(&m, item);
4990     }
4992     if (items) {
4993         g_slist_free(items);
4994     }
4996     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4997             _("Change connector spacing"));
4999     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5001     spinbutton_defocus(GTK_OBJECT(tbl));
5004 static void sp_connector_graph_layout(void)
5006     if (!SP_ACTIVE_DESKTOP) return;
5008     // hack for clones, see comment in align-and-distribute.cpp
5009     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5010     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5012     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5014     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5016     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5019 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5021     if ( gtk_toggle_action_get_active( act ) ) {
5022         prefs_set_string_attribute("tools.connector", "directedlayout",
5023                 "true");
5024     } else {
5025         prefs_set_string_attribute("tools.connector", "directedlayout",
5026                 "false");
5027     }
5030 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5032     if ( gtk_toggle_action_get_active( act ) ) {
5033         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5034                 "true");
5035     } else {
5036         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5037                 "false");
5038     }
5042 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5044     prefs_set_double_attribute("tools.connector", "length", adj->value);
5045     spinbutton_defocus(GTK_OBJECT(tbl));
5048 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5049                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5050                                             bool /*is_interactive*/, gpointer data)
5052     GtkWidget *tbl = GTK_WIDGET(data);
5054     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5055         return;
5056     }
5057     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5058         return;
5059     }
5061     GtkAdjustment *adj = (GtkAdjustment*)
5062             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5063     gdouble spacing = defaultConnSpacing;
5064     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5066     gtk_adjustment_set_value(adj, spacing);
5070 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5071     NULL, /* child_added */
5072     NULL, /* child_removed */
5073     connector_tb_event_attr_changed,
5074     NULL, /* content_changed */
5075     NULL  /* order_changed */
5076 };
5079 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5081     {
5082         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5083                                           _("Avoid"),
5084                                           _("Make connectors avoid selected objects"),
5085                                           "connector_avoid",
5086                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5087         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5088         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5089     }
5091     {
5092         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5093                                           _("Ignore"),
5094                                           _("Make connectors ignore selected objects"),
5095                                           "connector_ignore",
5096                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5097         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5098         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5099     }
5101     EgeAdjustmentAction* eact = 0;
5103     // Spacing spinbox
5104     eact = create_adjustment_action( "ConnectorSpacingAction",
5105                                      _("Connector Spacing"), _("Spacing:"),
5106                                      _("The amount of space left around objects by auto-routing connectors"),
5107                                      "tools.connector", "spacing", defaultConnSpacing,
5108                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5109                                      0, 100, 1.0, 10.0,
5110                                      0, 0, 0,
5111                                      connector_spacing_changed, 1, 0 );
5112     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5114     // Graph (connector network) layout
5115     {
5116         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5117                                           _("Graph"),
5118                                           _("Nicely arrange selected connector network"),
5119                                           "graph_layout",
5120                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5121         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5122         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5123     }
5125     // Default connector length spinbox
5126     eact = create_adjustment_action( "ConnectorLengthAction",
5127                                      _("Connector Length"), _("Length:"),
5128                                      _("Ideal length for connectors when layout is applied"),
5129                                      "tools.connector", "length", 100,
5130                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5131                                      10, 1000, 10.0, 100.0,
5132                                      0, 0, 0,
5133                                      connector_length_changed, 1, 0 );
5134     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5137     // Directed edges toggle button
5138     {
5139         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5140                                                       _("Downwards"),
5141                                                       _("Make connectors with end-markers (arrows) point downwards"),
5142                                                       "directed_graph",
5143                                                       Inkscape::ICON_SIZE_DECORATION );
5144         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5146         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5147         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5148                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5150         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5151     }
5153     // Avoid overlaps toggle button
5154     {
5155         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5156                                                       _("Remove overlaps"),
5157                                                       _("Do not allow overlapping shapes"),
5158                                                       "remove_overlaps",
5159                                                       Inkscape::ICON_SIZE_DECORATION );
5160         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5162         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5163         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5164                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5166         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5167     }
5169     // Code to watch for changes to the connector-spacing attribute in
5170     // the XML.
5171     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5172     g_assert(repr != NULL);
5174     purge_repr_listener( holder, holder );
5176     if (repr) {
5177         g_object_set_data( holder, "repr", repr );
5178         Inkscape::GC::anchor(repr);
5179         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5180         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5181     }
5182 } // end of sp_connector_toolbox_prep()
5185 //#########################
5186 //##     Paintbucket     ##
5187 //#########################
5189 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5191     gint channels = ege_select_one_action_get_active( act );
5192     flood_channels_set_channels( channels );
5195 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5197     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5200 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5202     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5205 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5207     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5208     SPUnit const *unit = tracker->getActiveUnit();
5210     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5212     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5215 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5217     // FIXME: make defaults settable via Inkscape Options
5218     struct KeyValue {
5219         char const *key;
5220         double value;
5221     } const key_values[] = {
5222         {"threshold", 15},
5223         {"offset", 0.0}
5224     };
5226     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5227         KeyValue const &kv = key_values[i];
5228         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5229         if ( adj ) {
5230             gtk_adjustment_set_value(adj, kv.value);
5231         }
5232     }
5234     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5235     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5236     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5237     ege_select_one_action_set_active( autogap_action, 0 );
5240 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5242     EgeAdjustmentAction* eact = 0;
5244     {
5245         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5247         GList* items = 0;
5248         gint count = 0;
5249         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5250         {
5251             GtkTreeIter iter;
5252             gtk_list_store_append( model, &iter );
5253             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5254             count++;
5255         }
5256         g_list_free( items );
5257         items = 0;
5258         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5259         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5260         ege_select_one_action_set_appearance( act1, "compact" );
5261         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5262         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5263         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5264         g_object_set_data( holder, "channels_action", act1 );
5265     }
5267     // Spacing spinbox
5268     {
5269         eact = create_adjustment_action(
5270             "ThresholdAction",
5271             _("Fill Threshold"), _("Threshold:"),
5272             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5273             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5274             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5275             0, 0, 0,
5276             paintbucket_threshold_changed, 1, 0 );
5278         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5279     }
5281     // Create the units menu.
5282     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5283     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5284     if (stored_unit)
5285         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5286     g_object_set_data( holder, "tracker", tracker );
5287     {
5288         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5289         gtk_action_group_add_action( mainActions, act );
5290     }
5292     // Offset spinbox
5293     {
5294         eact = create_adjustment_action(
5295             "OffsetAction",
5296             _("Grow/shrink by"), _("Grow/shrink by:"),
5297             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5298             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5299             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5300             0, 0, 0,
5301             paintbucket_offset_changed, 1, 2);
5302         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5304         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5305     }
5307     /* Auto Gap */
5308     {
5309         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5311         GList* items = 0;
5312         gint count = 0;
5313         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5314         {
5315             GtkTreeIter iter;
5316             gtk_list_store_append( model, &iter );
5317             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5318             count++;
5319         }
5320         g_list_free( items );
5321         items = 0;
5322         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5323         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5324         ege_select_one_action_set_appearance( act2, "compact" );
5325         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5326         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5327         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5328         g_object_set_data( holder, "autogap_action", act2 );
5329     }
5331     /* Reset */
5332     {
5333         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5334                                           _("Defaults"),
5335                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5336                                           GTK_STOCK_CLEAR );
5337         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5338         gtk_action_group_add_action( mainActions, act );
5339         gtk_action_set_sensitive( act, TRUE );
5340     }
5344 /*
5345   Local Variables:
5346   mode:c++
5347   c-file-style:"stroustrup"
5348   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5349   indent-tabs-mode:nil
5350   fill-column:99
5351   End:
5352 */
5353 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :