Code

a4d24203f95bfae6fe2ab1f8adba82ba69379c5b
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <gtkmm.h>
31 #include <gtk/gtk.h>
32 #include <iostream>
33 #include <sstream>
35 #include "widgets/button.h"
36 #include "widgets/widget-sizes.h"
37 #include "widgets/spw-utilities.h"
38 #include "widgets/spinbutton-events.h"
39 #include "dialogs/text-edit.h"
41 #include "ui/widget/style-swatch.h"
43 #include "prefs-utils.h"
44 #include "verbs.h"
45 #include "sp-namedview.h"
46 #include "desktop.h"
47 #include "desktop-handles.h"
48 #include "xml/repr.h"
49 #include "xml/node-event-vector.h"
50 #include <glibmm/i18n.h>
51 #include "helper/unit-menu.h"
52 #include "helper/units.h"
54 #include "inkscape.h"
55 #include "conn-avoid-ref.h"
58 #include "select-toolbar.h"
59 #include "gradient-toolbar.h"
61 #include "connector-context.h"
62 #include "node-context.h"
63 #include "shape-editor.h"
64 #include "tweak-context.h"
65 #include "sp-rect.h"
66 #include "box3d.h"
67 #include "box3d-context.h"
68 #include "sp-star.h"
69 #include "sp-spiral.h"
70 #include "sp-ellipse.h"
71 #include "sp-text.h"
72 #include "sp-flowtext.h"
73 #include "style.h"
74 #include "selection.h"
75 #include "document-private.h"
76 #include "desktop-style.h"
77 #include "../libnrtype/font-lister.h"
78 #include "../connection-pool.h"
79 #include "../prefs-utils.h"
80 #include "../inkscape-stock.h"
81 #include "icon.h"
82 #include "graphlayout/graphlayout.h"
84 #include "mod360.h"
86 #include "toolbox.h"
88 #include "flood-context.h"
90 #include "ink-action.h"
91 #include "ege-adjustment-action.h"
92 #include "ege-output-action.h"
93 #include "ege-select-one-action.h"
94 #include "helper/unit-tracker.h"
96 using Inkscape::UnitTracker;
98 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
99 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
101 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
102 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
103 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
114 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
120 static struct {
121     gchar const *type_name;
122     gchar const *data_name;
123     sp_verb_t verb;
124     sp_verb_t doubleclick_verb;
125 } const tools[] = {
126     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
127     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
128     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
129     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
130     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
131 //    { "SP3DBoxContext",    "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
132     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
133     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
134     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
135     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
136     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
137     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
138     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
139     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
140     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
141     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
142     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
143     { NULL, NULL, 0, 0 }
144 };
146 static struct {
147     gchar const *type_name;
148     gchar const *data_name;
149     GtkWidget *(*create_func)(SPDesktop *desktop);
150     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
151     gchar const *ui_name;
152     gint swatch_verb_id;
153     gchar const *swatch_tool;
154     gchar const *swatch_tip;
155 } const aux_toolboxes[] = {
156     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
157       SP_VERB_INVALID, 0, 0},
158     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
159       SP_VERB_INVALID, 0, 0},
160     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
161       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
162     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
163       SP_VERB_INVALID, 0, 0},
164     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
165       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
166     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
167       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
168     { "SP3DBoxContext",  "3dbox_toolbox",  0, sp_3dbox_toolbox_prep,             "3DBoxToolbar",
169       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
170     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
171       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
172     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
173       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
174     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
175       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
176     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
177       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
178     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
179       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
180     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
181       SP_VERB_INVALID, 0, 0},
182     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
183       SP_VERB_INVALID, 0, 0},
184     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
185       SP_VERB_INVALID, 0, 0},
186     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
187       SP_VERB_INVALID, 0, 0},
188     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
189       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
190     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
191 };
194 static gchar const * ui_descr =
195         "<ui>"
196         "  <toolbar name='SelectToolbar'>"
197         "    <toolitem action='EditSelectAll' />"
198         "    <toolitem action='EditSelectAllInAllLayers' />"
199         "    <toolitem action='EditDeselect' />"
200         "    <separator />"
201         "    <toolitem action='ObjectRotate90CCW' />"
202         "    <toolitem action='ObjectRotate90' />"
203         "    <toolitem action='ObjectFlipHorizontally' />"
204         "    <toolitem action='ObjectFlipVertically' />"
205         "    <separator />"
206         "    <toolitem action='SelectionToBack' />"
207         "    <toolitem action='SelectionLower' />"
208         "    <toolitem action='SelectionRaise' />"
209         "    <toolitem action='SelectionToFront' />"
210         "    <separator />"
211         "    <toolitem action='XAction' />"
212         "    <toolitem action='YAction' />"
213         "    <toolitem action='WidthAction' />"
214         "    <toolitem action='LockAction' />"
215         "    <toolitem action='HeightAction' />"
216         "    <toolitem action='UnitsAction' />"
217         "    <separator />"
218         "    <toolitem action='transform_affect_label' />"
219         "    <toolitem action='transform_stroke' />"
220         "    <toolitem action='transform_corners' />"
221         "    <toolitem action='transform_gradient' />"
222         "    <toolitem action='transform_pattern' />"
223         "  </toolbar>"
225         "  <toolbar name='NodeToolbar'>"
226         "    <toolitem action='NodeInsertAction' />"
227         "    <toolitem action='NodeDeleteAction' />"
228         "    <separator />"
229         "    <toolitem action='NodeJoinAction' />"
230         "    <toolitem action='NodeJoinSegmentAction' />"
231         "    <toolitem action='NodeDeleteSegmentAction' />"
232         "    <toolitem action='NodeBreakAction' />"
233         "    <separator />"
234         "    <toolitem action='NodeCuspAction' />"
235         "    <toolitem action='NodeSmoothAction' />"
236         "    <toolitem action='NodeSymmetricAction' />"
237         "    <separator />"
238         "    <toolitem action='NodeLineAction' />"
239         "    <toolitem action='NodeCurveAction' />"
240         "    <separator />"
241         "    <toolitem action='ObjectToPath' />"
242         "    <toolitem action='StrokeToPath' />"
243         "    <separator />"
244         "    <toolitem action='NodesShowHandlesAction' />"
245         "    <separator />"
246         "    <toolitem action='NodeXAction' />"
247         "    <toolitem action='NodeYAction' />"
248         "  </toolbar>"
250         "  <toolbar name='TweakToolbar'>"
251         "    <toolitem action='TweakWidthAction' />"
252         "    <separator />"
253         "    <toolitem action='TweakForceAction' />"
254         "    <toolitem action='TweakPressureAction' />"
255         "    <separator />"
256         "    <toolitem action='TweakModeAction' />"
257         "    <separator />"
258         "    <toolitem action='TweakFidelityAction' />"
259         "    <separator />"
260         "    <toolitem action='TweakChannelsLabel' />"
261         "    <toolitem action='TweakDoH' />"
262         "    <toolitem action='TweakDoS' />"
263         "    <toolitem action='TweakDoL' />"
264         "    <toolitem action='TweakDoO' />"
265         "  </toolbar>"
267         "  <toolbar name='ZoomToolbar'>"
268         "    <toolitem action='ZoomIn' />"
269         "    <toolitem action='ZoomOut' />"
270         "    <separator />"
271         "    <toolitem action='Zoom1:0' />"
272         "    <toolitem action='Zoom1:2' />"
273         "    <toolitem action='Zoom2:1' />"
274         "    <separator />"
275         "    <toolitem action='ZoomSelection' />"
276         "    <toolitem action='ZoomDrawing' />"
277         "    <toolitem action='ZoomPage' />"
278         "    <toolitem action='ZoomPageWidth' />"
279         "    <separator />"
280         "    <toolitem action='ZoomPrev' />"
281         "    <toolitem action='ZoomNext' />"
282         "  </toolbar>"
284         "  <toolbar name='StarToolbar'>"
285         "    <separator />"
286         "    <toolitem action='StarStateAction' />"
287         "    <separator />"
288         "    <toolitem action='FlatAction' />"
289         "    <separator />"
290         "    <toolitem action='MagnitudeAction' />"
291         "    <toolitem action='SpokeAction' />"
292         "    <toolitem action='RoundednessAction' />"
293         "    <toolitem action='RandomizationAction' />"
294         "    <separator />"
295         "    <toolitem action='StarResetAction' />"
296         "  </toolbar>"
298         "  <toolbar name='RectToolbar'>"
299         "    <toolitem action='RectStateAction' />"
300         "    <toolitem action='RectWidthAction' />"
301         "    <toolitem action='RectHeightAction' />"
302         "    <toolitem action='RadiusXAction' />"
303         "    <toolitem action='RadiusYAction' />"
304         "    <toolitem action='RectUnitsAction' />"
305         "    <separator />"
306         "    <toolitem action='RectResetAction' />"
307         "  </toolbar>"
309         "  <toolbar name='3DBoxToolbar'>"
310         "    <toolitem action='3DBoxPosAngleXAction' />"
311         "    <toolitem action='3DBoxVPXAction' />"
312         "    <separator />"
313         "    <toolitem action='3DBoxPosAngleYAction' />"
314         "    <toolitem action='3DBoxVPYAction' />"
315         "    <separator />"
316         "    <toolitem action='3DBoxPosAngleZAction' />"
317         "    <toolitem action='3DBoxVPZAction' />"
318         "    <separator />"
319         "  </toolbar>"
321         "  <toolbar name='SpiralToolbar'>"
322         "    <toolitem action='SpiralStateAction' />"
323         "    <toolitem action='SpiralRevolutionAction' />"
324         "    <toolitem action='SpiralExpansionAction' />"
325         "    <toolitem action='SpiralT0Action' />"
326         "    <separator />"
327         "    <toolitem action='SpiralResetAction' />"
328         "  </toolbar>"
330         "  <toolbar name='PenToolbar'>"
331         "  </toolbar>"
333         "  <toolbar name='PencilToolbar'>"
334         "  </toolbar>"
336         "  <toolbar name='CalligraphyToolbar'>"
337         "    <separator />"
338         "    <toolitem action='CalligraphyWidthAction' />"
339         "    <toolitem action='PressureAction' />"
340         "    <toolitem action='TraceAction' />"
341         "    <toolitem action='ThinningAction' />"
342         "    <separator />"
343         "    <toolitem action='AngleAction' />"
344         "    <toolitem action='TiltAction' />"
345         "    <toolitem action='FixationAction' />"
346         "    <separator />"
347         "    <toolitem action='CapRoundingAction' />"
348         "    <separator />"
349         "    <toolitem action='TremorAction' />"
350         "    <toolitem action='WiggleAction' />"
351         "    <toolitem action='MassAction' />"
352         "    <separator />"
353         "    <toolitem action='CalligraphyResetAction' />"
354         "  </toolbar>"
356         "  <toolbar name='ArcToolbar'>"
357         "    <toolitem action='ArcStateAction' />"
358         "    <separator />"
359         "    <toolitem action='ArcStartAction' />"
360         "    <toolitem action='ArcEndAction' />"
361         "    <separator />"
362         "    <toolitem action='ArcOpenAction' />"
363         "    <separator />"
364         "    <toolitem action='ArcResetAction' />"
365         "    <separator />"
366         "  </toolbar>"
368         "  <toolbar name='PaintbucketToolbar'>"
369         "    <toolitem action='ChannelsAction' />"
370         "    <separator />"
371         "    <toolitem action='ThresholdAction' />"
372         "    <separator />"
373         "    <toolitem action='OffsetAction' />"
374         "    <toolitem action='PaintbucketUnitsAction' />"
375         "    <separator />"
376         "    <toolitem action='AutoGapAction' />"
377         "    <separator />"
378         "    <toolitem action='PaintbucketResetAction' />"
379         "  </toolbar>"
381         "  <toolbar name='DropperToolbar'>"
382         "    <toolitem action='DropperPickAlphaAction' />"
383         "    <toolitem action='DropperSetAlphaAction' />"
384         "  </toolbar>"
386         "  <toolbar name='ConnectorToolbar'>"
387         "    <toolitem action='ConnectorAvoidAction' />"
388         "    <toolitem action='ConnectorIgnoreAction' />"
389         "    <toolitem action='ConnectorSpacingAction' />"
390         "    <toolitem action='ConnectorGraphAction' />"
391         "    <toolitem action='ConnectorLengthAction' />"
392         "    <toolitem action='ConnectorDirectedAction' />"
393         "    <toolitem action='ConnectorOverlapAction' />"
394         "  </toolbar>"
396         "</ui>"
399 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
401 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
403 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
404 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
406 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
407 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
409 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
410 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
412 /* Global text entry widgets necessary for update */
413 /* GtkWidget *dropper_rgb_entry,
414           *dropper_opacity_entry ; */
415 // should be made a private member once this is converted to class
417 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
418     connection->disconnect();
419     delete connection;
422 static void purge_repr_listener( GObject* obj, GObject* tbl )
424     (void)obj;
425     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
426     if (oldrepr) { // remove old listener
427         sp_repr_remove_listener_by_data(oldrepr, tbl);
428         Inkscape::GC::release(oldrepr);
429         oldrepr = 0;
430         g_object_set_data( tbl, "repr", NULL );
431     }
434 GtkWidget *
435 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
436                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
437                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
439     SPAction *action = verb->get_action(view);
440     if (!action) return NULL;
442     SPAction *doubleclick_action;
443     if (doubleclick_verb)
444         doubleclick_action = doubleclick_verb->get_action(view);
445     else
446         doubleclick_action = NULL;
448     /* fixme: Handle sensitive/unsensitive */
449     /* fixme: Implement sp_button_new_from_action */
450     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
451     gtk_widget_show(b);
452     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
454     return b;
457 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
458                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
460     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
463 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
464                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
466     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
470 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
472     SPAction* targetAction = SP_ACTION(user_data);
473     if ( targetAction ) {
474         sp_action_perform( targetAction, NULL );
475     }
478 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
480     if ( data ) {
481         GtkAction* act = GTK_ACTION(data);
482         gtk_action_set_sensitive( act, sensitive );
483     }
486 static SPActionEventVector action_event_vector = {
487     {NULL},
488     NULL,
489     NULL,
490     sp_action_action_set_sensitive,
491     NULL,
492     NULL
493 };
495 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
497     GtkAction* act = 0;
499     SPAction* targetAction = verb->get_action(view);
500     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
501     act = GTK_ACTION(inky);
502     gtk_action_set_sensitive( act, targetAction->sensitive );
504     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
506     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
507     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
509     return act;
512 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
514     Inkscape::UI::View::View *view = desktop;
515     gint verbsToUse[] = {
516         // disabled until we have icons for them:
517         //find
518         //SP_VERB_EDIT_TILE,
519         //SP_VERB_EDIT_UNTILE,
520         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
521         SP_VERB_DIALOG_DISPLAY,
522         SP_VERB_DIALOG_FILL_STROKE,
523         SP_VERB_DIALOG_NAMEDVIEW,
524         SP_VERB_DIALOG_TEXT,
525         SP_VERB_DIALOG_XML_EDITOR,
526         SP_VERB_EDIT_CLONE,
527         SP_VERB_EDIT_COPY,
528         SP_VERB_EDIT_CUT,
529         SP_VERB_EDIT_DUPLICATE,
530         SP_VERB_EDIT_PASTE,
531         SP_VERB_EDIT_REDO,
532         SP_VERB_EDIT_UNDO,
533         SP_VERB_EDIT_UNLINK_CLONE,
534         SP_VERB_FILE_EXPORT,
535         SP_VERB_FILE_IMPORT,
536         SP_VERB_FILE_NEW,
537         SP_VERB_FILE_OPEN,
538         SP_VERB_FILE_PRINT,
539         SP_VERB_FILE_SAVE,
540         SP_VERB_OBJECT_TO_CURVE,
541         SP_VERB_SELECTION_GROUP,
542         SP_VERB_SELECTION_OUTLINE,
543         SP_VERB_SELECTION_UNGROUP,
544         SP_VERB_ZOOM_1_1,
545         SP_VERB_ZOOM_1_2,
546         SP_VERB_ZOOM_2_1,
547         SP_VERB_ZOOM_DRAWING,
548         SP_VERB_ZOOM_IN,
549         SP_VERB_ZOOM_NEXT,
550         SP_VERB_ZOOM_OUT,
551         SP_VERB_ZOOM_PAGE,
552         SP_VERB_ZOOM_PAGE_WIDTH,
553         SP_VERB_ZOOM_PREV,
554         SP_VERB_ZOOM_SELECTION,
555     };
557     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
558     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
560     static std::map<SPDesktop*, GtkActionGroup*> groups;
561     GtkActionGroup* mainActions = 0;
562     if ( groups.find(desktop) != groups.end() ) {
563         mainActions = groups[desktop];
564     }
566     if ( !mainActions ) {
567         mainActions = gtk_action_group_new("main");
568         groups[desktop] = mainActions;
569     }
571     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
572         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
573         if ( verb ) {
574             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
575                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
576                 gtk_action_group_add_action( mainActions, act );
577             }
578         }
579     }
581     return mainActions;
585 GtkWidget *
586 sp_tool_toolbox_new()
588     GtkTooltips *tt = gtk_tooltips_new();
589     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
591     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
592     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
594     gtk_widget_set_sensitive(tb, FALSE);
596     GtkWidget *hb = gtk_handle_box_new();
597     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
598     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
599     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
601     gtk_container_add(GTK_CONTAINER(hb), tb);
602     gtk_widget_show(GTK_WIDGET(tb));
604     sigc::connection* conn = new sigc::connection;
605     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
607     return hb;
610 static void
611 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
613     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
614     gtk_widget_queue_resize(child);
617 static void
618 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
620     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
621     gtk_widget_queue_resize(child);
624 GtkWidget *
625 sp_aux_toolbox_new()
627     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
629     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
630     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
631     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
632     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
633     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
635     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
636     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
638     gtk_widget_set_sensitive(tb, FALSE);
640     GtkWidget *hb = gtk_handle_box_new();
641     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
642     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
643     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
645     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
646     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
648     gtk_container_add(GTK_CONTAINER(hb), tb);
649     gtk_widget_show(GTK_WIDGET(tb));
651     sigc::connection* conn = new sigc::connection;
652     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
654     return hb;
657 //####################################
658 //# Commands Bar
659 //####################################
661 GtkWidget *
662 sp_commands_toolbox_new()
664     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
666     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
667     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
668     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
669     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
670     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
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 /* is called when the node selection is modified */
848 static void
849 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
851     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
852     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
853     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
854     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
856     // quit if run by the attr_changed listener
857     if (g_object_get_data( tbl, "freeze" )) {
858         return;
859     }
861     // in turn, prevent listener from responding
862     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
864     ShapeEditor *shape_editor = get_current_shape_editor();
865     if (shape_editor && shape_editor->has_nodepath()) {
866         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
867         int n_selected = 0;
868         if (nodepath) {
869             n_selected = nodepath->numSelected();
870         }
872         if (n_selected == 0) {
873             gtk_action_set_sensitive(xact, FALSE);
874             gtk_action_set_sensitive(yact, FALSE);
875         } else {
876             gtk_action_set_sensitive(xact, TRUE);
877             gtk_action_set_sensitive(yact, TRUE);
878             NR::Coord oldx = gtk_adjustment_get_value(xadj);
879             NR::Coord oldy = gtk_adjustment_get_value(xadj);
881             if (n_selected == 1) {
882                 NR::Point sel_node = nodepath->singleSelectedCoords();
883                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
884                     gtk_adjustment_set_value(xadj, sel_node[NR::X]);
885                     gtk_adjustment_set_value(yadj, sel_node[NR::Y]);
886                 }
887             } else {
888                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
889                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
890                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
891                     /* Note: Currently x and y will always have a value, even if the coordinates of the
892                        selected nodes don't coincide (in this case we use the coordinates of the center
893                        of the bounding box). So the entries are never set to zero. */
894                     gtk_adjustment_set_value(xadj, x ? (*x) : 0.0); // FIXME: Maybe we should clear the entry
895                     gtk_adjustment_set_value(yadj, y ? (*y) : 0.0); //        fields, not set them to zero.
896                 }
897             }
898         }
899     }
901     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
904 static void
905 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
907     // quit if run by the attr_changed listener
908     if (g_object_get_data( tbl, "freeze" )) {
909         return;
910     }
912     // in turn, prevent listener from responding
913     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
915     ShapeEditor *shape_editor = get_current_shape_editor();
916     if (shape_editor && shape_editor->has_nodepath()) {
917         if (!strcmp(value_name, "x")) {
918             sp_node_selected_move_absolute(shape_editor->get_nodepath(), gtk_adjustment_get_value(adj), NR::X);
919         }
920         if (!strcmp(value_name, "y")) {
921             sp_node_selected_move_absolute(shape_editor->get_nodepath(), gtk_adjustment_get_value(adj), NR::Y);
922         }
923     }
925     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
928 static void
929 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
931     sp_node_path_value_changed(adj, tbl, "x");
934 static void
935 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
937     sp_node_path_value_changed(adj, tbl, "y");
940 //################################
941 //##    Node Editing Toolbox    ##
942 //################################
944 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
946     {
947         InkAction* inky = ink_action_new( "NodeInsertAction",
948                                           _("Insert node"),
949                                           _("Insert new nodes into selected segments"),
950                                           "node_insert",
951                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
952         g_object_set( inky, "short_label", _("Insert"), NULL );
953         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
954         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
955     }
957     {
958         InkAction* inky = ink_action_new( "NodeDeleteAction",
959                                           _("Delete node"),
960                                           _("Delete selected nodes"),
961                                           "node_delete",
962                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
963         g_object_set( inky, "short_label", _("Delete"), NULL );
964         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
965         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
966     }
968     {
969         InkAction* inky = ink_action_new( "NodeJoinAction",
970                                           _("Join endnodes"),
971                                           _("Join selected endnodes"),
972                                           "node_join",
973                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
974         g_object_set( inky, "short_label", _("Join"), NULL );
975         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
976         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
977     }
979     {
980         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
981                                           _("Join Segment"),
982                                           _("Join selected endnodes with a new segment"),
983                                           "node_join_segment",
984                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
985         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
986         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
987     }
989     {
990         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
991                                           _("Delete Segment"),
992                                           _("Split path between two non-endpoint nodes"),
993                                           "node_delete_segment",
994                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
995         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
996         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
997     }
999     {
1000         InkAction* inky = ink_action_new( "NodeBreakAction",
1001                                           _("Node Break"),
1002                                           _("Break path at selected nodes"),
1003                                           "node_break",
1004                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1005         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1006         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1007     }
1009     {
1010         InkAction* inky = ink_action_new( "NodeCuspAction",
1011                                           _("Node Cusp"),
1012                                           _("Make selected nodes corner"),
1013                                           "node_cusp",
1014                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1015         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1016         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1017     }
1019     {
1020         InkAction* inky = ink_action_new( "NodeSmoothAction",
1021                                           _("Node Smooth"),
1022                                           _("Make selected nodes smooth"),
1023                                           "node_smooth",
1024                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1025         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1026         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1027     }
1029     {
1030         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1031                                           _("Node Symmetric"),
1032                                           _("Make selected nodes symmetric"),
1033                                           "node_symmetric",
1034                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1035         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1036         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1037     }
1039     {
1040         InkAction* inky = ink_action_new( "NodeLineAction",
1041                                           _("Node Line"),
1042                                           _("Make selected segments lines"),
1043                                           "node_line",
1044                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1045         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1046         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1047     }
1049     {
1050         InkAction* inky = ink_action_new( "NodeCurveAction",
1051                                           _("Node Curve"),
1052                                           _("Make selected segments curves"),
1053                                           "node_curve",
1054                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1055         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1056         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1057     }
1059     {
1060         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1061                                                       _("Show Handles"),
1062                                                       _("Show the Bezier handles of selected nodes"),
1063                                                       "nodes_show_handles",
1064                                                       Inkscape::ICON_SIZE_DECORATION );
1065         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1066         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1067         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1068     }
1070     /* X coord of selected node(s) */
1071     {
1072         EgeAdjustmentAction* eact = 0;
1073         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1074         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1075         eact = create_adjustment_action( "NodeXAction",
1076                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1077                                          "tools.nodes", "Xcoord", 0,
1078                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1079                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1080                                          labels, values, G_N_ELEMENTS(labels),
1081                                          sp_node_path_x_value_changed );
1082         g_object_set_data( holder, "nodes_x_action", eact );
1083         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1084         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1085     }
1087     /* Y coord of selected node(s) */
1088     {
1089         EgeAdjustmentAction* eact = 0;
1090         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1091         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1092         eact = create_adjustment_action( "NodeYAction",
1093                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1094                                          "tools.nodes", "Ycoord", 0,
1095                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1096                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1097                                          labels, values, G_N_ELEMENTS(labels),
1098                                          sp_node_path_y_value_changed );
1099         g_object_set_data( holder, "nodes_y_action", eact );
1100         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1101         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1102     }
1104     sigc::connection *connection = new sigc::connection (
1105         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1106         );
1108     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1109     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1110 } // end of sp_node_toolbox_prep()
1113 //########################
1114 //##    Zoom Toolbox    ##
1115 //########################
1117 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1119     // no custom GtkAction setup needed
1120 } // end of sp_zoom_toolbox_prep()
1122 void
1123 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1125     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")));
1129 void
1130 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1132     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")));
1135 void
1136 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1138     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")));
1141 static void
1142 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1144     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1145     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1147     if (old_desktop) {
1148         GList *children, *iter;
1150         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1151         for ( iter = children ; iter ; iter = iter->next ) {
1152             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1153         }
1154         g_list_free(children);
1155     }
1157     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1159     if (desktop) {
1160         gtk_widget_set_sensitive(toolbox, TRUE);
1161         setup_func(toolbox, desktop);
1162         update_func(desktop, desktop->event_context, toolbox);
1163         *conn = desktop->connectEventContextChanged
1164             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1165     } else {
1166         gtk_widget_set_sensitive(toolbox, FALSE);
1167     }
1169 } // end of toolbox_set_desktop()
1172 static void
1173 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1175     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1176     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1177     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1178         // "toolbox.tools" was not set. Fallback to older value
1179         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1181         // Copy the setting forwards
1182         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1183     }
1184     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1186     for (int i = 0 ; tools[i].type_name ; i++ ) {
1187         GtkWidget *button =
1188             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1189                                                               SP_BUTTON_TYPE_TOGGLE,
1190                                                               Inkscape::Verb::get(tools[i].verb),
1191                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1192                                                               desktop,
1193                                                               tooltips );
1195         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1196                            (gpointer)button );
1197     }
1201 static void
1202 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1204     gchar const *const tname = ( eventcontext
1205                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1206                                  : NULL );
1207     for (int i = 0 ; tools[i].type_name ; i++ ) {
1208         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1209         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1210     }
1213 static void
1214 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1216     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1217     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1218     GtkUIManager* mgr = gtk_ui_manager_new();
1219     GError* errVal = 0;
1220     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1221     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1223     std::map<std::string, GtkWidget*> dataHolders;
1225     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1226         if ( aux_toolboxes[i].prep_func ) {
1227             // converted to GtkActions and UIManager
1229             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1230             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1231             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1232             dataHolders[aux_toolboxes[i].type_name] = kludge;
1233             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1234         } else {
1236             GtkWidget *sub_toolbox = 0;
1237             if (aux_toolboxes[i].create_func == NULL)
1238                 sub_toolbox = sp_empty_toolbox_new(desktop);
1239             else {
1240                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1241             }
1243             gtk_size_group_add_widget( grouper, sub_toolbox );
1245             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1246             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1248         }
1249     }
1251     // Second pass to create toolbars *after* all GtkActions are created
1252     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1253         if ( aux_toolboxes[i].prep_func ) {
1254             // converted to GtkActions and UIManager
1256             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1258             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1259             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1261             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1262             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1263             g_free( tmp );
1264             tmp = 0;
1266             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1267             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1268             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1269                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1270             }
1271             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1274             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1276             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1277                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1278                 swatch->setDesktop( desktop );
1279                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1280                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1281                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1282                 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 );
1283             }
1285             gtk_widget_show_all( holder );
1286             sp_set_font_size_smaller( holder );
1288             gtk_size_group_add_widget( grouper, holder );
1290             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1291             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1292         }
1293     }
1295     g_object_unref( G_OBJECT(grouper) );
1298 static void
1299 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1301     gchar const *tname = ( eventcontext
1302                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1303                            : NULL );
1304     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1305         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1306         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1307             gtk_widget_show_all(sub_toolbox);
1308             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1309         } else {
1310             gtk_widget_hide(sub_toolbox);
1311         }
1312     }
1315 static void
1316 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1318     gchar const * descr =
1319         "<ui>"
1320         "  <toolbar name='CommandsToolbar'>"
1321         "    <toolitem action='FileNew' />"
1322         "    <toolitem action='FileOpen' />"
1323         "    <toolitem action='FileSave' />"
1324         "    <toolitem action='FilePrint' />"
1325         "    <separator />"
1326         "    <toolitem action='FileImport' />"
1327         "    <toolitem action='FileExport' />"
1328         "    <separator />"
1329         "    <toolitem action='EditUndo' />"
1330         "    <toolitem action='EditRedo' />"
1331         "    <separator />"
1332         "    <toolitem action='EditCopy' />"
1333         "    <toolitem action='EditCut' />"
1334         "    <toolitem action='EditPaste' />"
1335         "    <separator />"
1336         "    <toolitem action='ZoomSelection' />"
1337         "    <toolitem action='ZoomDrawing' />"
1338         "    <toolitem action='ZoomPage' />"
1339         "    <separator />"
1340         "    <toolitem action='EditDuplicate' />"
1341         "    <toolitem action='EditClone' />"
1342         "    <toolitem action='EditUnlinkClone' />"
1343         "    <separator />"
1344         "    <toolitem action='SelectionGroup' />"
1345         "    <toolitem action='SelectionUnGroup' />"
1346         "    <separator />"
1347         "    <toolitem action='DialogFillStroke' />"
1348         "    <toolitem action='DialogText' />"
1349         "    <toolitem action='DialogXMLEditor' />"
1350         "    <toolitem action='DialogAlignDistribute' />"
1351         "    <separator />"
1352         "    <toolitem action='DialogPreferences' />"
1353         "    <toolitem action='DialogDocumentProperties' />"
1354         "  </toolbar>"
1355         "</ui>";
1356     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1359     GtkUIManager* mgr = gtk_ui_manager_new();
1360     GError* errVal = 0;
1362     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1363     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1365     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1366     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1367         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1368     }
1369     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1370     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1371     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1374     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1377 static void
1378 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1382 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1384     gtk_widget_show(toolbox_toplevel);
1385     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1387     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1388     if (!shown_toolbox) {
1389         return;
1390     }
1391     gtk_widget_show(toolbox);
1393     // need to show the spacer, or the padding will be off
1394     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1395     gtk_widget_show(spacer);
1397     gtk_widget_show_all(shown_toolbox);
1400 void
1401 aux_toolbox_space(GtkWidget *tb, gint space)
1403     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1406 static GtkWidget *
1407 sp_empty_toolbox_new(SPDesktop *desktop)
1409     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1410     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1411     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1413     gtk_widget_show_all(tbl);
1414     sp_set_font_size_smaller (tbl);
1416     return tbl;
1419 // helper UI functions
1421 GtkWidget *
1422 sp_tb_spinbutton(
1423     gchar *label, gchar const *tooltip,
1424     gchar const *path, gchar const *data, gdouble def,
1425     GtkWidget *us,
1426     GtkWidget *tbl,
1427     gboolean altx, gchar const *altx_mark,
1428     gdouble lower, gdouble upper, gdouble step, gdouble page,
1429     void (*callback)(GtkAdjustment *, GtkWidget *),
1430     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1432     GtkTooltips *tt = gtk_tooltips_new();
1434     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1436     GtkWidget *l = gtk_label_new(label);
1437     gtk_widget_show(l);
1438     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1439     gtk_container_add(GTK_CONTAINER(hb), l);
1441     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1442                                       lower, upper, step, page, page);
1443     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1444     if (us)
1445         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1447     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1448     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1449     if (altx)
1450         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1451     gtk_widget_set_size_request(sb,
1452                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1453                                 AUX_SPINBUTTON_HEIGHT);
1454     gtk_widget_show(sb);
1455     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1456     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1457     gtk_container_add(GTK_CONTAINER(hb), sb);
1458     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1460     return hb;
1463 #define MODE_LABEL_WIDTH 70
1465 //########################
1466 //##       Star         ##
1467 //########################
1469 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1471     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1473     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1474         // do not remember prefs if this call is initiated by an undo change, because undoing object
1475         // creation sets bogus values to its attributes before it is deleted
1476         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1477     }
1479     // quit if run by the attr_changed listener
1480     if (g_object_get_data( dataKludge, "freeze" )) {
1481         return;
1482     }
1484     // in turn, prevent listener from responding
1485     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1487     bool modmade = false;
1489     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1490     GSList const *items = selection->itemList();
1491     for (; items != NULL; items = items->next) {
1492         if (SP_IS_STAR((SPItem *) items->data)) {
1493             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1494             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1495             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1496                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1497                                     + M_PI / (gint)adj->value));
1498             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1499             modmade = true;
1500         }
1501     }
1502     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1503                                    _("Star: Change number of corners"));
1505     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1508 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1510     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1512     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1513         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1514     }
1516     // quit if run by the attr_changed listener
1517     if (g_object_get_data( dataKludge, "freeze" )) {
1518         return;
1519     }
1521     // in turn, prevent listener from responding
1522     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1524     bool modmade = false;
1525     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1526     GSList const *items = selection->itemList();
1527     for (; items != NULL; items = items->next) {
1528         if (SP_IS_STAR((SPItem *) items->data)) {
1529             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1531             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1532             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1533             if (r2 < r1) {
1534                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1535             } else {
1536                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1537             }
1539             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1540             modmade = true;
1541         }
1542     }
1544     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1545                                    _("Star: Change spoke ratio"));
1547     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1550 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1552     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1553     bool flat = ege_select_one_action_get_active( act ) == 0;
1555     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1556         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1557                                     flat ? "true" : "false" );
1558     }
1560     // quit if run by the attr_changed listener
1561     if (g_object_get_data( dataKludge, "freeze" )) {
1562         return;
1563     }
1565     // in turn, prevent listener from responding
1566     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1568     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1569     GSList const *items = selection->itemList();
1570     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1571     bool modmade = false;
1573     if ( prop_action ) {
1574         gtk_action_set_sensitive( prop_action, !flat );
1575     }
1577     for (; items != NULL; items = items->next) {
1578         if (SP_IS_STAR((SPItem *) items->data)) {
1579             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1580             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1581             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1582             modmade = true;
1583         }
1584     }
1586     if (modmade) {
1587         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1588                          flat ? _("Make polygon") : _("Make star"));
1589     }
1591     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1594 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1596     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1598     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1599         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1600     }
1602     // quit if run by the attr_changed listener
1603     if (g_object_get_data( dataKludge, "freeze" )) {
1604         return;
1605     }
1607     // in turn, prevent listener from responding
1608     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1610     bool modmade = false;
1612     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1613     GSList const *items = selection->itemList();
1614     for (; items != NULL; items = items->next) {
1615         if (SP_IS_STAR((SPItem *) items->data)) {
1616             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1617             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1618             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1619             modmade = true;
1620         }
1621     }
1622     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1623                                    _("Star: Change rounding"));
1625     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1628 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1630     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1632     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1633         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1634     }
1636     // quit if run by the attr_changed listener
1637     if (g_object_get_data( dataKludge, "freeze" )) {
1638         return;
1639     }
1641     // in turn, prevent listener from responding
1642     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1644     bool modmade = false;
1646     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1647     GSList const *items = selection->itemList();
1648     for (; items != NULL; items = items->next) {
1649         if (SP_IS_STAR((SPItem *) items->data)) {
1650             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1651             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1652             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1653             modmade = true;
1654         }
1655     }
1656     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1657                                    _("Star: Change randomization"));
1659     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1663 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1664                                        gchar const */*old_value*/, gchar const */*new_value*/,
1665                                        bool /*is_interactive*/, gpointer data)
1667     GtkWidget *tbl = GTK_WIDGET(data);
1669     // quit if run by the _changed callbacks
1670     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1671         return;
1672     }
1674     // in turn, prevent callbacks from responding
1675     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1677     GtkAdjustment *adj = 0;
1679     if (!strcmp(name, "inkscape:randomized")) {
1680         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1681         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1682     } else if (!strcmp(name, "inkscape:rounded")) {
1683         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1684         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1685     } else if (!strcmp(name, "inkscape:flatsided")) {
1686         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1687         char const *flatsides = repr->attribute("inkscape:flatsided");
1688         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1689         if ( flatsides && !strcmp(flatsides,"false") ) {
1690             ege_select_one_action_set_active( flat_action, 1 );
1691             gtk_action_set_sensitive( prop_action, TRUE );
1692         } else {
1693             ege_select_one_action_set_active( flat_action, 0 );
1694             gtk_action_set_sensitive( prop_action, FALSE );
1695         }
1696     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1697         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1698         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1699         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1700         if (r2 < r1) {
1701             gtk_adjustment_set_value(adj, r2/r1);
1702         } else {
1703             gtk_adjustment_set_value(adj, r1/r2);
1704         }
1705     } else if (!strcmp(name, "sodipodi:sides")) {
1706         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1707         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1708     }
1710     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1714 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1716     NULL, /* child_added */
1717     NULL, /* child_removed */
1718     star_tb_event_attr_changed,
1719     NULL, /* content_changed */
1720     NULL  /* order_changed */
1721 };
1724 /**
1725  *  \param selection Should not be NULL.
1726  */
1727 static void
1728 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1730     int n_selected = 0;
1731     Inkscape::XML::Node *repr = NULL;
1733     purge_repr_listener( tbl, tbl );
1735     for (GSList const *items = selection->itemList();
1736          items != NULL;
1737          items = items->next)
1738     {
1739         if (SP_IS_STAR((SPItem *) items->data)) {
1740             n_selected++;
1741             repr = SP_OBJECT_REPR((SPItem *) items->data);
1742         }
1743     }
1745     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1747     if (n_selected == 0) {
1748         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1749     } else if (n_selected == 1) {
1750         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1752         if (repr) {
1753             g_object_set_data( tbl, "repr", repr );
1754             Inkscape::GC::anchor(repr);
1755             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1756             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1757         }
1758     } else {
1759         // FIXME: implement averaging of all parameters for multiple selected stars
1760         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1761         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1762     }
1766 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1768     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1769     // callbacks to lump all the changes for all selected objects in one undo step
1771     GtkAdjustment *adj = 0;
1773     // fixme: make settable in prefs!
1774     gint mag = 5;
1775     gdouble prop = 0.5;
1776     gboolean flat = FALSE;
1777     gdouble randomized = 0;
1778     gdouble rounded = 0;
1780     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1781     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1783     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1784     gtk_action_set_sensitive( sb2, !flat );
1786     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1787     gtk_adjustment_set_value(adj, mag);
1788     gtk_adjustment_value_changed(adj);
1790     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1791     gtk_adjustment_set_value(adj, prop);
1792     gtk_adjustment_value_changed(adj);
1794     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1795     gtk_adjustment_set_value(adj, rounded);
1796     gtk_adjustment_value_changed(adj);
1798     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1799     gtk_adjustment_set_value(adj, randomized);
1800     gtk_adjustment_value_changed(adj);
1804 void
1805 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1807     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1808     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1809     GtkWidget *l = gtk_label_new(NULL);
1810     gtk_label_set_markup(GTK_LABEL(l), title);
1811     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1812     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1813     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1817 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1819     {
1820         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1821         ege_output_action_set_use_markup( act, TRUE );
1822         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1823         g_object_set_data( holder, "mode_action", act );
1824     }
1826     {
1827         //EgeAdjustmentAction* calligraphy_angle = 0;
1828         EgeAdjustmentAction* eact = 0;
1829         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1830         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1832         /* Flatsided checkbox */
1833         {
1834             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1836             GtkTreeIter iter;
1837             gtk_list_store_append( model, &iter );
1838             gtk_list_store_set( model, &iter,
1839                                 0, _("Polygon"),
1840                                 1, _("Regular polygon (with one handle) instead of a star"),
1841                                 2, "star_flat",
1842                                 -1 );
1844             gtk_list_store_append( model, &iter );
1845             gtk_list_store_set( model, &iter,
1846                                 0, _("Star"),
1847                                 1, _("Star instead of a regular polygon (with one handle)"),
1848                                 2, "star_angled",
1849                                 -1 );
1851             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1852             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1853             g_object_set_data( holder, "flat_action", act );
1855             ege_select_one_action_set_appearance( act, "full" );
1856             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1857             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1858             ege_select_one_action_set_icon_column( act, 2 );
1859             ege_select_one_action_set_tooltip_column( act, 1  );
1861             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1862             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1863         }
1865         /* Magnitude */
1866         {
1867         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1868         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1869         eact = create_adjustment_action( "MagnitudeAction",
1870                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1871                                          "tools.shapes.star", "magnitude", 3,
1872                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1873                                          3, 1024, 1, 5,
1874                                          labels, values, G_N_ELEMENTS(labels),
1875                                          sp_stb_magnitude_value_changed,
1876                                          1.0, 0 );
1877         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1878         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1879         }
1881         /* Spoke ratio */
1882         {
1883         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1884         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1885         eact = create_adjustment_action( "SpokeAction",
1886                                          _("Spoke ratio"), _("Spoke ratio:"),
1887                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1888                                          // Base radius is the same for the closest handle.
1889                                          _("Base radius to tip radius ratio"),
1890                                          "tools.shapes.star", "proportion", 0.5,
1891                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1892                                          0.01, 1.0, 0.01, 0.1,
1893                                          labels, values, G_N_ELEMENTS(labels),
1894                                          sp_stb_proportion_value_changed );
1895         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1896         g_object_set_data( holder, "prop_action", eact );
1897         }
1899         if ( !isFlatSided ) {
1900             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1901         } else {
1902             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1903         }
1905         /* Roundedness */
1906         {
1907         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1908         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1909         eact = create_adjustment_action( "RoundednessAction",
1910                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1911                                          "tools.shapes.star", "rounded", 0.0,
1912                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1913                                          -10.0, 10.0, 0.01, 0.1,
1914                                          labels, values, G_N_ELEMENTS(labels),
1915                                          sp_stb_rounded_value_changed );
1916         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1917         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1918         }
1920         /* Randomization */
1921         {
1922         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1923         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1924         eact = create_adjustment_action( "RandomizationAction",
1925                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1926                                          "tools.shapes.star", "randomized", 0.0,
1927                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1928                                          -10.0, 10.0, 0.001, 0.01,
1929                                          labels, values, G_N_ELEMENTS(labels),
1930                                          sp_stb_randomized_value_changed, 0.1, 3 );
1931         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1932         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1933         }
1934     }
1936     {
1937         /* Reset */
1938         {
1939             GtkAction* act = gtk_action_new( "StarResetAction",
1940                                              _("Defaults"),
1941                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1942                                              GTK_STOCK_CLEAR );
1943             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1944             gtk_action_group_add_action( mainActions, act );
1945             gtk_action_set_sensitive( act, TRUE );
1946         }
1947     }
1949     sigc::connection *connection = new sigc::connection(
1950         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1951         );
1952     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1953     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1957 //########################
1958 //##       Rect         ##
1959 //########################
1961 static void sp_rtb_sensitivize( GObject *tbl )
1963     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1964     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
1965     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
1967     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
1968         gtk_action_set_sensitive( not_rounded, FALSE );
1969     } else {
1970         gtk_action_set_sensitive( not_rounded, TRUE );
1971     }
1975 static void
1976 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
1977                           void (*setter)(SPRect *, gdouble))
1979     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1981     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1982     SPUnit const *unit = tracker->getActiveUnit();
1984     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1985         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
1986     }
1988     // quit if run by the attr_changed listener
1989     if (g_object_get_data( tbl, "freeze" )) {
1990         return;
1991     }
1993     // in turn, prevent listener from responding
1994     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1996     bool modmade = false;
1997     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1998     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
1999         if (SP_IS_RECT(items->data)) {
2000             if (adj->value != 0) {
2001                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2002             } else {
2003                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2004             }
2005             modmade = true;
2006         }
2007     }
2009     sp_rtb_sensitivize( tbl );
2011     if (modmade) {
2012         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2013                                    _("Change rectangle"));
2014     }
2016     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2019 static void
2020 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2022     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2025 static void
2026 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2028     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2031 static void
2032 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2034     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2037 static void
2038 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2040     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2045 static void
2046 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2048     GtkAdjustment *adj = 0;
2050     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2051     gtk_adjustment_set_value(adj, 0.0);
2052     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2053     gtk_adjustment_value_changed(adj);
2055     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2056     gtk_adjustment_set_value(adj, 0.0);
2057     gtk_adjustment_value_changed(adj);
2059     sp_rtb_sensitivize( obj );
2062 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2063                                        gchar const */*old_value*/, gchar const */*new_value*/,
2064                                        bool /*is_interactive*/, gpointer data)
2066     GObject *tbl = G_OBJECT(data);
2068     // quit if run by the _changed callbacks
2069     if (g_object_get_data( tbl, "freeze" )) {
2070         return;
2071     }
2073     // in turn, prevent callbacks from responding
2074     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2076     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2077     SPUnit const *unit = tracker->getActiveUnit();
2079     gpointer item = g_object_get_data( tbl, "item" );
2080     if (item && SP_IS_RECT(item)) {
2081         {
2082             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2083             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2084             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2085         }
2087         {
2088             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2089             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2090             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2091         }
2093         {
2094             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2095             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2096             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2097         }
2099         {
2100             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2101             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2102             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2103         }
2104     }
2106     sp_rtb_sensitivize( tbl );
2108     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2112 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2113     NULL, /* child_added */
2114     NULL, /* child_removed */
2115     rect_tb_event_attr_changed,
2116     NULL, /* content_changed */
2117     NULL  /* order_changed */
2118 };
2120 /**
2121  *  \param selection should not be NULL.
2122  */
2123 static void
2124 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2126     int n_selected = 0;
2127     Inkscape::XML::Node *repr = NULL;
2128     SPItem *item = NULL;
2130     if ( g_object_get_data( tbl, "repr" ) ) {
2131         g_object_set_data( tbl, "item", NULL );
2132     }
2133     purge_repr_listener( tbl, tbl );
2135     for (GSList const *items = selection->itemList();
2136          items != NULL;
2137          items = items->next) {
2138         if (SP_IS_RECT((SPItem *) items->data)) {
2139             n_selected++;
2140             item = (SPItem *) items->data;
2141             repr = SP_OBJECT_REPR(item);
2142         }
2143     }
2145     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2147     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2149     if (n_selected == 0) {
2150         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2152         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2153         gtk_action_set_sensitive(w, FALSE);
2154         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2155         gtk_action_set_sensitive(h, FALSE);
2157     } else if (n_selected == 1) {
2158         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2159         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2161         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2162         gtk_action_set_sensitive(w, TRUE);
2163         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2164         gtk_action_set_sensitive(h, TRUE);
2166         if (repr) {
2167             g_object_set_data( tbl, "repr", repr );
2168             g_object_set_data( tbl, "item", item );
2169             Inkscape::GC::anchor(repr);
2170             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2171             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2172         }
2173     } else {
2174         // FIXME: implement averaging of all parameters for multiple selected
2175         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2176         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2177         sp_rtb_sensitivize( tbl );
2178     }
2182 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2184     EgeAdjustmentAction* eact = 0;
2186     {
2187         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2188         ege_output_action_set_use_markup( act, TRUE );
2189         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2190         g_object_set_data( holder, "mode_action", act );
2191     }
2193     // rx/ry units menu: create
2194     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2195     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2196     // fixme: add % meaning per cent of the width/height
2197     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2198     g_object_set_data( holder, "tracker", tracker );
2200     /* W */
2201     {
2202         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2203         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2204         eact = create_adjustment_action( "RectWidthAction",
2205                                          _("Width"), _("W:"), _("Width of rectangle"),
2206                                          "tools.shapes.rect", "width", 0,
2207                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2208                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2209                                          labels, values, G_N_ELEMENTS(labels),
2210                                          sp_rtb_width_value_changed );
2211         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2212         g_object_set_data( holder, "width_action", eact );
2213         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2214         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2215     }
2217     /* H */
2218     {
2219         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2220         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2221         eact = create_adjustment_action( "RectHeightAction",
2222                                          _("Height"), _("H:"), _("Height of rectangle"),
2223                                          "tools.shapes.rect", "height", 0,
2224                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2225                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2226                                          labels, values, G_N_ELEMENTS(labels),
2227                                          sp_rtb_height_value_changed );
2228         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2229         g_object_set_data( holder, "height_action", eact );
2230         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2231         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2232     }
2234     /* rx */
2235     {
2236         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2237         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2238         eact = create_adjustment_action( "RadiusXAction",
2239                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2240                                          "tools.shapes.rect", "rx", 0,
2241                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2242                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2243                                          labels, values, G_N_ELEMENTS(labels),
2244                                          sp_rtb_rx_value_changed);
2245         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2246         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2247     }
2249     /* ry */
2250     {
2251         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2252         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2253         eact = create_adjustment_action( "RadiusYAction",
2254                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2255                                          "tools.shapes.rect", "ry", 0,
2256                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2257                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2258                                          labels, values, G_N_ELEMENTS(labels),
2259                                          sp_rtb_ry_value_changed);
2260         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2261         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2262     }
2264     // add the units menu
2265     {
2266         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2267         gtk_action_group_add_action( mainActions, act );
2268     }
2270     /* Reset */
2271     {
2272         InkAction* inky = ink_action_new( "RectResetAction",
2273                                           _("Not rounded"),
2274                                           _("Make corners sharp"),
2275                                           "squared_corner",
2276                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2277         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2278         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2279         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2280         g_object_set_data( holder, "not_rounded", inky );
2281     }
2283     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2284     sp_rtb_sensitivize( holder );
2286     sigc::connection *connection = new sigc::connection(
2287         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2288         );
2289     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2290     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2293 //########################
2294 //##       3D Box       ##
2295 //########################
2297 static void sp_3dbox_toggle_vp_changed (GtkToggleAction */*act*/, GObject *dataKludge, Box3D::Axis axis)
2299     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2300     SPDocument *document = sp_desktop_document (desktop);
2301     Box3D::Perspective3D *persp = document->current_perspective;
2303     g_return_if_fail (is_single_axis_direction (axis));
2304     g_return_if_fail (persp);
2306     persp->toggle_boxes (axis);
2308     gchar *str;
2309     switch (axis) {
2310         case Box3D::X:
2311             str = g_strdup ("box3d_angle_x_action");
2312             break;
2313         case Box3D::Y:
2314             str = g_strdup ("box3d_angle_y_action");
2315             break;
2316         case Box3D::Z:
2317             str = g_strdup ("box3d_angle_z_action");
2318             break;
2319         default:
2320             return;
2321     }
2322     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2323     if (angle_action) {
2324         gtk_action_set_sensitive (angle_action, !persp->get_vanishing_point (axis)->is_finite() );
2325     }
2327     // FIXME: Given how it is realized in the other tools, this is probably not the right way to do it,
2328     //        but without the if construct, we get continuous segfaults. Needs further investigation.
2329     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2330         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
2331                          _("3D Box: Change perspective"));
2332     }
2335 static void sp_3dbox_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2337     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::X);
2340 static void sp_3dbox_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2342     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Y);
2345 static void sp_3dbox_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2347     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Z);
2350 static void sp_3dbox_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Box3D::Axis axis )
2352     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2353     Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2355     if (persp) {
2356         double angle = adj->value * M_PI/180;
2357         persp->set_infinite_direction (axis, NR::Point (cos (angle), sin (angle)));
2359         // FIXME: See comment above; without the if construct we get segfaults during undo.
2360         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2361             sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2362                              _("3D Box: Change perspective"));
2363         }
2364     }
2365     //g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
2368 static void sp_3dbox_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2370     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::X);
2373 static void sp_3dbox_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2375     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Y);
2378 static void sp_3dbox_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2380     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Z);
2383 // normalize angle so that it lies in the interval [0,360]
2384 static double sp_3dbox_normalize_angle (double a) {
2385     double angle = a + ((int) (a/360.0))*360;
2386     if (angle < 0) {
2387         angle += 360.0;
2388     }
2389     return angle;
2392 static void sp_3dbox_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *name,
2393                                            gchar const */*old_value*/, gchar const */*new_value*/,
2394                                            bool /*is_interactive*/, gpointer data)
2396     GtkWidget *tbl = GTK_WIDGET(data);
2398     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2399     /***
2400     // quit if run by the _changed callbacks
2401     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2402         return;
2403     }
2405     // in turn, prevent callbacks from responding
2406     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2407     ***/
2409     if (!strcmp(name, "inkscape:perspective")) {
2410         GtkAdjustment *adj = 0;
2411         double angle;
2412         SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2413         Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2415         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2416         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::X)->get_angle());
2417         gtk_adjustment_set_value(adj, angle);
2419         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2420         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Y)->get_angle());
2421         gtk_adjustment_set_value(adj, angle);
2423         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2424         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Z)->get_angle());
2425         gtk_adjustment_set_value(adj, angle);
2426     }
2429 static Inkscape::XML::NodeEventVector sp_3dbox_tb_repr_events =
2431     NULL, /* child_added */
2432     NULL, /* child_removed */
2433     sp_3dbox_tb_event_attr_changed,
2434     NULL, /* content_changed */
2435     NULL  /* order_changed */
2436 };
2438 /**
2439  *  \param selection Should not be NULL.
2440  */
2441 static void
2442 sp_3dbox_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2444     Inkscape::XML::Node *repr = NULL;
2445     purge_repr_listener(tbl, tbl);
2447     SPItem *item = selection->singleItem();
2448     if (item) {
2449         repr = SP_OBJECT_REPR(item);
2450         if (repr) {
2451             g_object_set_data(tbl, "repr", repr);
2452             Inkscape::GC::anchor(repr);
2453             sp_repr_add_listener(repr, &sp_3dbox_tb_repr_events, tbl);
2454             sp_repr_synthesize_events(repr, &sp_3dbox_tb_repr_events, tbl);
2455         }
2456     }
2459 static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2461     EgeAdjustmentAction* eact = 0;
2462     SPDocument *document = sp_desktop_document (desktop);
2463     Box3D::Perspective3D *persp = document->current_perspective;
2464     bool toggled = false;
2466     /* angle of VP in X direction */
2467     eact = create_adjustment_action("3DBoxPosAngleXAction",
2468                                     _("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2469                                     "tools.shapes.3dbox", "dir_vp_x", persp->get_vanishing_point (Box3D::X)->get_angle(),
2470                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2471                                     0.0, 360.0, 1.0, 10.0,
2472                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2473                                     sp_3dbox_vpx_angle_changed,
2474                                     0.1, 1);
2475     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2476     g_object_set_data(holder, "box3d_angle_x_action", eact);
2477     if (!persp->get_vanishing_point (Box3D::X)->is_finite()) {
2478         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2479     } else {
2480         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2481     }
2483     /* toggle VP in X direction */
2484     {
2485     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2486                                                   _("Toggle VP in X direction"),
2487                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2488                                                   "toggle_vp_x",
2489                                                   Inkscape::ICON_SIZE_DECORATION);
2490     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2491     if (persp) {
2492         toggled = !persp->get_vanishing_point(Box3D::X)->is_finite();
2493     }
2494     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2495     /* we connect the signal after setting the state to avoid switching the state again */
2496     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_x_changed), holder);
2497     }
2499     /* angle of VP in Y direction */
2500     eact = create_adjustment_action("3DBoxPosAngleYAction",
2501                                     _("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2502                                     "tools.shapes.3dbox", "dir_vp_y", persp->get_vanishing_point (Box3D::Y)->get_angle(),
2503                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2504                                     0.0, 360.0, 1.0, 10.0,
2505                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2506                                     sp_3dbox_vpy_angle_changed,
2507                                     0.1, 1);
2508     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2509     g_object_set_data(holder, "box3d_angle_y_action", eact);
2510     if (!persp->get_vanishing_point (Box3D::Y)->is_finite()) {
2511         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2512     } else {
2513         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2514     }
2516     /* toggle VP in Y direction */
2517     {
2518     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2519                                                  _("Toggle VP in Y direction"),
2520                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2521                                                  "toggle_vp_y",
2522                                                  Inkscape::ICON_SIZE_DECORATION);
2523     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2524     if (persp) {
2525         toggled = !persp->get_vanishing_point(Box3D::Y)->is_finite();
2526     }
2527     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2528     /* we connect the signal after setting the state to avoid switching the state again */
2529     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_y_changed), holder);
2530     }
2532     /* angle of VP in Z direction */
2533     eact = create_adjustment_action("3DBoxPosAngleZAction",
2534                                     _("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2535                                     "tools.shapes.3dbox", "dir_vp_z", persp->get_vanishing_point (Box3D::Z)->get_angle(),
2536                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2537                                      0.0, 360.0, 1.0, 10.0,
2538                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2539                                     sp_3dbox_vpz_angle_changed,
2540                                     0.1, 1);
2542     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2543     g_object_set_data(holder, "box3d_angle_z_action", eact);
2544     if (!persp->get_vanishing_point (Box3D::Z)->is_finite()) {
2545         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2546     } else {
2547         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2548     }
2550     /* toggle VP in Z direction */
2551     {
2552     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2553                                                  _("Toggle VP in Z direction"),
2554                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2555                                                  "toggle_vp_z",
2556                                                  Inkscape::ICON_SIZE_DECORATION);
2557     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2558     if (persp) {
2559         toggled = !persp->get_vanishing_point(Box3D::Z)->is_finite();
2560     }
2561     /* we connect the signal after setting the state to avoid switching the state again */
2562     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2563     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_z_changed), holder);
2564     }
2566     sigc::connection *connection = new sigc::connection(
2567         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_3dbox_toolbox_selection_changed), (GObject *)holder))
2568        );
2569     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2570     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2573 //########################
2574 //##       Spiral       ##
2575 //########################
2577 static void
2578 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2580     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2582     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2583         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2584     }
2586     // quit if run by the attr_changed listener
2587     if (g_object_get_data( tbl, "freeze" )) {
2588         return;
2589     }
2591     // in turn, prevent listener from responding
2592     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2594     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2596     bool modmade = false;
2597     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2598          items != NULL;
2599          items = items->next)
2600     {
2601         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2602             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2603             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2604             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2605             modmade = true;
2606         }
2607     }
2609     g_free(namespaced_name);
2611     if (modmade) {
2612         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2613                                    _("Change spiral"));
2614     }
2616     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2619 static void
2620 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2622     sp_spl_tb_value_changed(adj, tbl, "revolution");
2625 static void
2626 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2628     sp_spl_tb_value_changed(adj, tbl, "expansion");
2631 static void
2632 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2634     sp_spl_tb_value_changed(adj, tbl, "t0");
2637 static void
2638 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2640     GtkWidget *tbl = GTK_WIDGET(obj);
2642     GtkAdjustment *adj;
2644     // fixme: make settable
2645     gdouble rev = 5;
2646     gdouble exp = 1.0;
2647     gdouble t0 = 0.0;
2649     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2650     gtk_adjustment_set_value(adj, rev);
2651     gtk_adjustment_value_changed(adj);
2653     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2654     gtk_adjustment_set_value(adj, exp);
2655     gtk_adjustment_value_changed(adj);
2657     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2658     gtk_adjustment_set_value(adj, t0);
2659     gtk_adjustment_value_changed(adj);
2661     spinbutton_defocus(GTK_OBJECT(tbl));
2665 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2666                                          gchar const */*old_value*/, gchar const */*new_value*/,
2667                                          bool /*is_interactive*/, gpointer data)
2669     GtkWidget *tbl = GTK_WIDGET(data);
2671     // quit if run by the _changed callbacks
2672     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2673         return;
2674     }
2676     // in turn, prevent callbacks from responding
2677     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2679     GtkAdjustment *adj;
2680     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2681     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2683     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2684     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2686     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2687     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2689     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2693 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2694     NULL, /* child_added */
2695     NULL, /* child_removed */
2696     spiral_tb_event_attr_changed,
2697     NULL, /* content_changed */
2698     NULL  /* order_changed */
2699 };
2701 static void
2702 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2704     int n_selected = 0;
2705     Inkscape::XML::Node *repr = NULL;
2707     purge_repr_listener( tbl, tbl );
2709     for (GSList const *items = selection->itemList();
2710          items != NULL;
2711          items = items->next)
2712     {
2713         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2714             n_selected++;
2715             repr = SP_OBJECT_REPR((SPItem *) items->data);
2716         }
2717     }
2719     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2721     if (n_selected == 0) {
2722         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2723     } else if (n_selected == 1) {
2724         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2726         if (repr) {
2727             g_object_set_data( tbl, "repr", repr );
2728             Inkscape::GC::anchor(repr);
2729             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2730             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2731         }
2732     } else {
2733         // FIXME: implement averaging of all parameters for multiple selected
2734         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2735         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2736     }
2740 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2742     EgeAdjustmentAction* eact = 0;
2744     {
2745         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2746         ege_output_action_set_use_markup( act, TRUE );
2747         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2748         g_object_set_data( holder, "mode_action", act );
2749     }
2751     /* Revolution */
2752     {
2753         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2754         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2755         eact = create_adjustment_action( "SpiralRevolutionAction",
2756                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2757                                          "tools.shapes.spiral", "revolution", 3.0,
2758                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2759                                          0.01, 1024.0, 0.1, 1.0,
2760                                          labels, values, G_N_ELEMENTS(labels),
2761                                          sp_spl_tb_revolution_value_changed, 1, 2);
2762         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2763     }
2765     /* Expansion */
2766     {
2767         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2768         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2769         eact = create_adjustment_action( "SpiralExpansionAction",
2770                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2771                                          "tools.shapes.spiral", "expansion", 1.0,
2772                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2773                                          0.0, 1000.0, 0.01, 1.0,
2774                                          labels, values, G_N_ELEMENTS(labels),
2775                                          sp_spl_tb_expansion_value_changed);
2776         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2777     }
2779     /* T0 */
2780     {
2781         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2782         gdouble values[] = {0, 0.5, 0.9};
2783         eact = create_adjustment_action( "SpiralT0Action",
2784                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2785                                          "tools.shapes.spiral", "t0", 0.0,
2786                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2787                                          0.0, 0.999, 0.01, 1.0,
2788                                          labels, values, G_N_ELEMENTS(labels),
2789                                          sp_spl_tb_t0_value_changed);
2790         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2791     }
2793     /* Reset */
2794     {
2795         InkAction* inky = ink_action_new( "SpiralResetAction",
2796                                           _("Defaults"),
2797                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2798                                           GTK_STOCK_CLEAR,
2799                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2800         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2801         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2802     }
2805     sigc::connection *connection = new sigc::connection(
2806         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2807         );
2808     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2809     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2812 //########################
2813 //##     Pen/Pencil    ##
2814 //########################
2817 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2819     // Put stuff here
2822 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2824     // Put stuff here
2827 //########################
2828 //##       Tweak        ##
2829 //########################
2831 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2833     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2836 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2838     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2841 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2843     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2846 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2848     int mode = ege_select_one_action_get_active( act );
2849     prefs_set_int_attribute("tools.tweak", "mode", mode);
2851     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2852     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2853     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2854     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2855     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2856     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2857     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2858         if (doh) gtk_action_set_sensitive (doh, TRUE);
2859         if (dos) gtk_action_set_sensitive (dos, TRUE);
2860         if (dol) gtk_action_set_sensitive (dol, TRUE);
2861         if (doo) gtk_action_set_sensitive (doo, TRUE);
2862         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2863         if (fid) gtk_action_set_sensitive (fid, FALSE);
2864     } else {
2865         if (doh) gtk_action_set_sensitive (doh, FALSE);
2866         if (dos) gtk_action_set_sensitive (dos, FALSE);
2867         if (dol) gtk_action_set_sensitive (dol, FALSE);
2868         if (doo) gtk_action_set_sensitive (doo, FALSE);
2869         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2870         if (fid) gtk_action_set_sensitive (fid, TRUE);
2871     }
2874 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2876     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2879 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2880     bool show = gtk_toggle_action_get_active( act );
2881     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2883 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2884     bool show = gtk_toggle_action_get_active( act );
2885     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2887 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
2888     bool show = gtk_toggle_action_get_active( act );
2889     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2891 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
2892     bool show = gtk_toggle_action_get_active( act );
2893     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
2896 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2898     {
2899         /* Width */
2900         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2901         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2902         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2903                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2904                                                               "tools.tweak", "width", 15,
2905                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2906                                                               1, 100, 1.0, 10.0,
2907                                                               labels, values, G_N_ELEMENTS(labels),
2908                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2909         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2910         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2911     }
2914     {
2915         /* Force */
2916         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2917         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2918         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2919                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
2920                                                               "tools.tweak", "force", 20,
2921                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2922                                                               1, 100, 1.0, 10.0,
2923                                                               labels, values, G_N_ELEMENTS(labels),
2924                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2925         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2926         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2927     }
2929     /* Mode */
2930     {
2931         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2933         GtkTreeIter iter;
2934         gtk_list_store_append( model, &iter );
2935         gtk_list_store_set( model, &iter,
2936                             0, _("Push mode"),
2937                             1, _("Push parts of paths in any direction"),
2938                             2, "tweak_push_mode",
2939                             -1 );
2941         gtk_list_store_append( model, &iter );
2942         gtk_list_store_set( model, &iter,
2943                             0, _("Shrink mode"),
2944                             1, _("Shrink (inset) parts of paths"),
2945                             2, "tweak_shrink_mode",
2946                             -1 );
2948         gtk_list_store_append( model, &iter );
2949         gtk_list_store_set( model, &iter,
2950                             0, _("Grow mode"),
2951                             1, _("Grow (outset) parts of paths"),
2952                             2, "tweak_grow_mode",
2953                             -1 );
2955         gtk_list_store_append( model, &iter );
2956         gtk_list_store_set( model, &iter,
2957                             0, _("Attract mode"),
2958                             1, _("Attract parts of paths towards cursor"),
2959                             2, "tweak_attract_mode",
2960                             -1 );
2962         gtk_list_store_append( model, &iter );
2963         gtk_list_store_set( model, &iter,
2964                             0, _("Repel mode"),
2965                             1, _("Repel parts of paths from cursor"),
2966                             2, "tweak_repel_mode",
2967                             -1 );
2969         gtk_list_store_append( model, &iter );
2970         gtk_list_store_set( model, &iter,
2971                             0, _("Roughen mode"),
2972                             1, _("Roughen parts of paths"),
2973                             2, "tweak_roughen_mode",
2974                             -1 );
2976         gtk_list_store_append( model, &iter );
2977         gtk_list_store_set( model, &iter,
2978                             0, _("Color paint mode"),
2979                             1, _("Paint the tool's color upon selected objects"),
2980                             2, "tweak_colorpaint_mode",
2981                             -1 );
2983         gtk_list_store_append( model, &iter );
2984         gtk_list_store_set( model, &iter,
2985                             0, _("Color jitter mode"),
2986                             1, _("Jitter the colors of selected objects"),
2987                             2, "tweak_colorjitter_mode",
2988                             -1 );
2990         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
2991         g_object_set( act, "short_label", _("Mode:"), NULL );
2992         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2993         g_object_set_data( holder, "mode_action", act );
2995         ege_select_one_action_set_appearance( act, "full" );
2996         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2997         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2998         ege_select_one_action_set_icon_column( act, 2 );
2999         ege_select_one_action_set_tooltip_column( act, 1  );
3001         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3002         ege_select_one_action_set_active( act, mode );
3003         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3005         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3006     }
3008     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3010     {
3011         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3012         ege_output_action_set_use_markup( act, TRUE );
3013         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3014         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3015             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3016         g_object_set_data( holder, "tweak_channels_label", act);
3017     }
3019     {
3020         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3021                                                       _("Hue"),
3022                                                       _("In color mode, act on objects' hue"),
3023                                                       NULL,
3024                                                       Inkscape::ICON_SIZE_DECORATION );
3025         g_object_set( act, "short_label", _("H"), NULL );
3026         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3027         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3028         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3029         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3030             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3031         g_object_set_data( holder, "tweak_doh", act);
3032     }
3033     {
3034         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3035                                                       _("Saturation"),
3036                                                       _("In color mode, act on objects' saturation"),
3037                                                       NULL,
3038                                                       Inkscape::ICON_SIZE_DECORATION );
3039         g_object_set( act, "short_label", _("S"), NULL );
3040         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3041         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3042         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3043         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3044             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3045         g_object_set_data( holder, "tweak_dos", act );
3046     }
3047     {
3048         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3049                                                       _("Lightness"),
3050                                                       _("In color mode, act on objects' lightness"),
3051                                                       NULL,
3052                                                       Inkscape::ICON_SIZE_DECORATION );
3053         g_object_set( act, "short_label", _("L"), NULL );
3054         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3055         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3056         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3057         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3058             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3059         g_object_set_data( holder, "tweak_dol", act );
3060     }
3061     {
3062         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3063                                                       _("Opacity"),
3064                                                       _("In color mode, act on objects' opacity"),
3065                                                       NULL,
3066                                                       Inkscape::ICON_SIZE_DECORATION );
3067         g_object_set( act, "short_label", _("O"), NULL );
3068         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3069         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3070         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3071         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3072             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3073         g_object_set_data( holder, "tweak_doo", act );
3074     }
3076     {   /* Fidelity */
3077         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3078         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3079         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3080                                                               _("Fidelity"), _("Fidelity:"),
3081                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3082                                                               "tools.tweak", "fidelity", 50,
3083                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3084                                                               1, 100, 1.0, 10.0,
3085                                                               labels, values, G_N_ELEMENTS(labels),
3086                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3087         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3088         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3089         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3090             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3091         g_object_set_data( holder, "tweak_fidelity", eact );
3092     }
3095     /* Use Pressure button */
3096     {
3097         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3098                                                       _("Pressure"),
3099                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3100                                                       "use_pressure",
3101                                                       Inkscape::ICON_SIZE_DECORATION );
3102         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3103         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3104         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3105     }
3110 //########################
3111 //##     Calligraphy    ##
3112 //########################
3114 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3116     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3119 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3121     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3124 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3126     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3129 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3131     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3134 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3136     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3139 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3141     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3144 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3146     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3149 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3151     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3154 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3156     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3159 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3161     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3164 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3166     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3168     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3171 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3173     // FIXME: make defaults settable via Inkscape Options
3174     struct KeyValue {
3175         char const *key;
3176         double value;
3177     } const key_values[] = {
3178         {"mass", 0.02},
3179         {"wiggle", 0.0},
3180         {"angle", 30.0},
3181         {"width", 15},
3182         {"thinning", 0.1},
3183         {"tremor", 0.0},
3184         {"flatness", 0.9},
3185         {"cap_rounding", 0.0}
3186     };
3188     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3189         KeyValue const &kv = key_values[i];
3190         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3191         if ( adj ) {
3192             gtk_adjustment_set_value(adj, kv.value);
3193         }
3194     }
3198 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3200     {
3201         EgeAdjustmentAction* calligraphy_angle = 0;
3203         {
3204         /* Width */
3205         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3206         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3207         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3208                                                               _("Pen Width"), _("Width:"),
3209                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3210                                                               "tools.calligraphic", "width", 15,
3211                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3212                                                               1, 100, 1.0, 10.0,
3213                                                               labels, values, G_N_ELEMENTS(labels),
3214                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3215         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3216         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3217         }
3219         {
3220         /* Thinning */
3221             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3222             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3223         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3224                                                               _("Stroke Thinning"), _("Thinning:"),
3225                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3226                                                               "tools.calligraphic", "thinning", 0.1,
3227                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3228                                                               -1.0, 1.0, 0.01, 0.1,
3229                                                               labels, values, G_N_ELEMENTS(labels),
3230                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3231         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3232         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3233         }
3235         {
3236         /* Angle */
3237         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3238         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3239         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3240                                                               _("Pen Angle"), _("Angle:"),
3241                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3242                                                               "tools.calligraphic", "angle", 30,
3243                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3244                                                               -90.0, 90.0, 1.0, 10.0,
3245                                                               labels, values, G_N_ELEMENTS(labels),
3246                                                               sp_ddc_angle_value_changed, 1, 0 );
3247         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3248         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3249         calligraphy_angle = eact;
3250         }
3252         {
3253         /* Fixation */
3254             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3255         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3256         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3257                                                               _("Fixation"), _("Fixation:"),
3258                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3259                                                               "tools.calligraphic", "flatness", 0.9,
3260                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3261                                                               0.0, 1.0, 0.01, 0.1,
3262                                                               labels, values, G_N_ELEMENTS(labels),
3263                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3264         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3265         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3266         }
3268         {
3269         /* Cap Rounding */
3270             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3271         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3272         // TRANSLATORS: "cap" means "end" (both start and finish) here
3273         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3274                                                               _("Cap rounding"), _("Caps:"),
3275                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3276                                                               "tools.calligraphic", "cap_rounding", 0.0,
3277                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3278                                                               0.0, 5.0, 0.01, 0.1,
3279                                                               labels, values, G_N_ELEMENTS(labels),
3280                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3281         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3282         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3283         }
3285         {
3286         /* Tremor */
3287             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3288         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3289         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3290                                                               _("Stroke Tremor"), _("Tremor:"),
3291                                                               _("Increase to make strokes rugged and trembling"),
3292                                                               "tools.calligraphic", "tremor", 0.0,
3293                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3294                                                               0.0, 1.0, 0.01, 0.1,
3295                                                               labels, values, G_N_ELEMENTS(labels),
3296                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3298         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3299         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3300         }
3302         {
3303         /* Wiggle */
3304         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3305         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3306         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3307                                                               _("Pen Wiggle"), _("Wiggle:"),
3308                                                               _("Increase to make the pen waver and wiggle"),
3309                                                               "tools.calligraphic", "wiggle", 0.0,
3310                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3311                                                               0.0, 1.0, 0.01, 0.1,
3312                                                               labels, values, G_N_ELEMENTS(labels),
3313                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3314         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3315         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3316         }
3318         {
3319         /* Mass */
3320             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3321         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3322         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3323                                                               _("Pen Mass"), _("Mass:"),
3324                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3325                                                               "tools.calligraphic", "mass", 0.02,
3326                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3327                                                               0.0, 1.0, 0.01, 0.1,
3328                                                               labels, values, G_N_ELEMENTS(labels),
3329                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3330         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3331         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3332         }
3335         /* Trace Background button */
3336         {
3337             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3338                                                           _("Trace Background"),
3339                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3340                                                           "trace_background",
3341                                                           Inkscape::ICON_SIZE_DECORATION );
3342             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3343             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3344             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3345         }
3347         /* Use Pressure button */
3348         {
3349             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3350                                                           _("Pressure"),
3351                                                           _("Use the pressure of the input device to alter the width of the pen"),
3352                                                           "use_pressure",
3353                                                           Inkscape::ICON_SIZE_DECORATION );
3354             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3355             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3356             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3357         }
3359         /* Use Tilt button */
3360         {
3361             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3362                                                           _("Tilt"),
3363                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3364                                                           "use_tilt",
3365                                                           Inkscape::ICON_SIZE_DECORATION );
3366             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3367             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3368             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3369             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3370         }
3372         /* Reset */
3373         {
3374             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3375                                              _("Defaults"),
3376                                              _("Reset all parameters to defaults"),
3377                                              GTK_STOCK_CLEAR );
3378             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3379             gtk_action_group_add_action( mainActions, act );
3380             gtk_action_set_sensitive( act, TRUE );
3381         }
3382     }
3386 //########################
3387 //##    Circle / Arc    ##
3388 //########################
3390 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3392     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3393     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3395     if (v1 == 0 && v2 == 0) {
3396         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3397             gtk_action_set_sensitive( ocb, FALSE );
3398             gtk_action_set_sensitive( make_whole, FALSE );
3399         }
3400     } else {
3401         gtk_action_set_sensitive( ocb, TRUE );
3402         gtk_action_set_sensitive( make_whole, TRUE );
3403     }
3406 static void
3407 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3409     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3411     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3412         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3413     }
3415     // quit if run by the attr_changed listener
3416     if (g_object_get_data( tbl, "freeze" )) {
3417         return;
3418     }
3420     // in turn, prevent listener from responding
3421     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3423     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3425     bool modmade = false;
3426     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3427          items != NULL;
3428          items = items->next)
3429     {
3430         SPItem *item = SP_ITEM(items->data);
3432         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3434             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3435             SPArc *arc = SP_ARC(item);
3437             if (!strcmp(value_name, "start"))
3438                 ge->start = (adj->value * M_PI)/ 180;
3439             else
3440                 ge->end = (adj->value * M_PI)/ 180;
3442             sp_genericellipse_normalize(ge);
3443             ((SPObject *)arc)->updateRepr();
3444             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3446             modmade = true;
3447         }
3448     }
3450     g_free(namespaced_name);
3452     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3454     sp_arctb_sensitivize( tbl, adj->value, other->value );
3456     if (modmade) {
3457         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3458                                    _("Arc: Change start/end"));
3459     }
3461     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3465 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3467     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3470 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3472     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3475 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3477     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3478     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3479         if ( ege_select_one_action_get_active( act ) != 0 ) {
3480             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3481         } else {
3482             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3483         }
3484     }
3486     // quit if run by the attr_changed listener
3487     if (g_object_get_data( tbl, "freeze" )) {
3488         return;
3489     }
3491     // in turn, prevent listener from responding
3492     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3494     bool modmade = false;
3496     if ( ege_select_one_action_get_active(act) != 0 ) {
3497         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3498              items != NULL;
3499              items = items->next)
3500         {
3501             if (SP_IS_ARC((SPItem *) items->data)) {
3502                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3503                 repr->setAttribute("sodipodi:open", "true");
3504                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3505                 modmade = true;
3506             }
3507         }
3508     } else {
3509         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3510              items != NULL;
3511              items = items->next)
3512         {
3513             if (SP_IS_ARC((SPItem *) items->data))    {
3514                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3515                 repr->setAttribute("sodipodi:open", NULL);
3516                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3517                 modmade = true;
3518             }
3519         }
3520     }
3522     if (modmade) {
3523         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3524                                    _("Arc: Change open/closed"));
3525     }
3527     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3530 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3532     GtkAdjustment *adj;
3533     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3534     gtk_adjustment_set_value(adj, 0.0);
3535     gtk_adjustment_value_changed(adj);
3537     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3538     gtk_adjustment_set_value(adj, 0.0);
3539     gtk_adjustment_value_changed(adj);
3541     spinbutton_defocus( GTK_OBJECT(obj) );
3544 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3545                                       gchar const */*old_value*/, gchar const */*new_value*/,
3546                                       bool /*is_interactive*/, gpointer data)
3548     GObject *tbl = G_OBJECT(data);
3550     // quit if run by the _changed callbacks
3551     if (g_object_get_data( tbl, "freeze" )) {
3552         return;
3553     }
3555     // in turn, prevent callbacks from responding
3556     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3558     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3559     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3561     GtkAdjustment *adj1,*adj2;
3562     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3563     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3564     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3565     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3567     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3569     char const *openstr = NULL;
3570     openstr = repr->attribute("sodipodi:open");
3571     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3573     if (openstr) {
3574         ege_select_one_action_set_active( ocb, 1 );
3575     } else {
3576         ege_select_one_action_set_active( ocb, 0 );
3577     }
3579     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3582 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3583     NULL, /* child_added */
3584     NULL, /* child_removed */
3585     arc_tb_event_attr_changed,
3586     NULL, /* content_changed */
3587     NULL  /* order_changed */
3588 };
3591 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3593     int n_selected = 0;
3594     Inkscape::XML::Node *repr = NULL;
3596     purge_repr_listener( tbl, tbl );
3598     for (GSList const *items = selection->itemList();
3599          items != NULL;
3600          items = items->next)
3601     {
3602         if (SP_IS_ARC((SPItem *) items->data)) {
3603             n_selected++;
3604             repr = SP_OBJECT_REPR((SPItem *) items->data);
3605         }
3606     }
3608     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3610     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3611     if (n_selected == 0) {
3612         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3613     } else if (n_selected == 1) {
3614         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3615         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3617         if (repr) {
3618             g_object_set_data( tbl, "repr", repr );
3619             Inkscape::GC::anchor(repr);
3620             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3621             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3622         }
3623     } else {
3624         // FIXME: implement averaging of all parameters for multiple selected
3625         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3626         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3627         sp_arctb_sensitivize( tbl, 1, 0 );
3628     }
3632 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3634     EgeAdjustmentAction* eact = 0;
3637     {
3638         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3639         ege_output_action_set_use_markup( act, TRUE );
3640         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3641         g_object_set_data( holder, "mode_action", act );
3642     }
3644     /* Start */
3645     {
3646         eact = create_adjustment_action( "ArcStartAction",
3647                                          _("Start"), _("Start:"),
3648                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3649                                          "tools.shapes.arc", "start", 0.0,
3650                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3651                                          -360.0, 360.0, 1.0, 10.0,
3652                                          0, 0, 0,
3653                                          sp_arctb_start_value_changed);
3654         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3655     }
3657     /* End */
3658     {
3659         eact = create_adjustment_action( "ArcEndAction",
3660                                          _("End"), _("End:"),
3661                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3662                                          "tools.shapes.arc", "end", 0.0,
3663                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3664                                          -360.0, 360.0, 1.0, 10.0,
3665                                          0, 0, 0,
3666                                          sp_arctb_end_value_changed);
3667         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3668     }
3670     /* Segments / Pie checkbox */
3671     {
3672         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3674         GtkTreeIter iter;
3675         gtk_list_store_append( model, &iter );
3676         gtk_list_store_set( model, &iter,
3677                             0, _("Closed arc"),
3678                             1, _("Switch to segment (closed shape with two radii)"),
3679                             2, "circle_closed_arc",
3680                             -1 );
3682         gtk_list_store_append( model, &iter );
3683         gtk_list_store_set( model, &iter,
3684                             0, _("Open Arc"),
3685                             1, _("Switch to arc (unclosed shape)"),
3686                             2, "circle_open_arc",
3687                             -1 );
3689         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3690         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3691         g_object_set_data( holder, "open_action", act );
3693         ege_select_one_action_set_appearance( act, "full" );
3694         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3695         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3696         ege_select_one_action_set_icon_column( act, 2 );
3697         ege_select_one_action_set_tooltip_column( act, 1  );
3699         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3700         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3701         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3702         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3703     }
3705     /* Make Whole */
3706     {
3707         InkAction* inky = ink_action_new( "ArcResetAction",
3708                                           _("Make whole"),
3709                                           _("Make the shape a whole ellipse, not arc or segment"),
3710                                           "reset_circle",
3711                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3712         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3713         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3714         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3715         g_object_set_data( holder, "make_whole", inky );
3716     }
3718     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3719     // sensitivize make whole and open checkbox
3720     {
3721         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3722         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3723         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3724     }
3727     sigc::connection *connection = new sigc::connection(
3728         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3729         );
3730     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3731     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3737 // toggle button callbacks and updaters
3739 //########################
3740 //##      Dropper       ##
3741 //########################
3743 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3744     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3745     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3746     if ( set_action ) {
3747         if ( gtk_toggle_action_get_active( act ) ) {
3748             gtk_action_set_sensitive( set_action, TRUE );
3749         } else {
3750             gtk_action_set_sensitive( set_action, FALSE );
3751         }
3752     }
3754     spinbutton_defocus(GTK_OBJECT(tbl));
3757 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3758     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3759     spinbutton_defocus(GTK_OBJECT(tbl));
3763 /**
3764  * Dropper auxiliary toolbar construction and setup.
3765  *
3766  * TODO: Would like to add swatch of current color.
3767  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3768  *       can drag and drop places. Will provide a nice mixing palette.
3769  */
3770 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3772     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3774     {
3775         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3776                                                       _("Pick alpha"),
3777                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3778                                                       "color_alpha_get",
3779                                                       Inkscape::ICON_SIZE_DECORATION );
3780         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3781         g_object_set_data( holder, "pick_action", act );
3782         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3783         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3784     }
3786     {
3787         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3788                                                       _("Set alpha"),
3789                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3790                                                       "color_alpha_set",
3791                                                       Inkscape::ICON_SIZE_DECORATION );
3792         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3793         g_object_set_data( holder, "set_action", act );
3794         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3795         // make sure it's disabled if we're not picking alpha
3796         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3797         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3798     }
3802 //########################
3803 //##    Text Toolbox    ##
3804 //########################
3805 /*
3806 static void
3807 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3809     //Call back for letter sizing spinbutton
3812 static void
3813 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3815     //Call back for line height spinbutton
3818 static void
3819 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3821     //Call back for horizontal kerning spinbutton
3824 static void
3825 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3827     //Call back for vertical kerning spinbutton
3830 static void
3831 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3833     //Call back for letter rotation spinbutton
3834 }*/
3836 namespace {
3838 bool visible = false;
3840 void
3841 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3843     SPStyle *query =
3844         sp_style_new (SP_ACTIVE_DOCUMENT);
3846     int result_family =
3847         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3849     int result_style =
3850         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3852     int result_numbers =
3853         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3855     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3857     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3858     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3859     {
3860         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3862         if (repr)
3863         {
3864             sp_style_read_from_repr (query, repr);
3865         }
3866         else
3867         {
3868             return;
3869         }
3870     }
3872     if (query->text)
3873     {
3874         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3875             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3876             gtk_entry_set_text (GTK_ENTRY (entry), "");
3878         } else if (query->text->font_family.value) {
3880             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3881             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3883             Gtk::TreePath path;
3884             try {
3885                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3886             } catch (...) {
3887                 return;
3888             }
3890             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3891             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3893             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3895             gtk_tree_selection_select_path (tselection, path.gobj());
3896             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3898             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3899         }
3901         //Size
3902         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3903         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3904         g_object_set_data (tbl, "size-block", gpointer(1));
3905         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3906         g_object_set_data (tbl, "size-block", gpointer(0));
3907         free (str);
3909         //Anchor
3910         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3911         {
3912             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3913             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3914             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3915             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3916         }
3917         else
3918         {
3919             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3920             {
3921                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3922                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3923                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3924                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3925             }
3926             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3927             {
3928                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3929                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3930                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3931                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3932             }
3933             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3934             {
3935                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3936                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3937                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3938                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3939             }
3940         }
3942         //Style
3943         {
3944             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3946             gboolean active = gtk_toggle_button_get_active (button);
3947             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3949             if (active != check)
3950             {
3951                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3952                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3953                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3954             }
3955         }
3957         {
3958             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
3960             gboolean active = gtk_toggle_button_get_active (button);
3961             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
3963             if (active != check)
3964             {
3965                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3966                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
3967                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3968             }
3969         }
3971         //Orientation
3972         //locking both buttons, changing one affect all group (both)
3973         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
3974         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3976         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
3977         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
3979         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
3980         {
3981             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3982         }
3983         else
3984         {
3985             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
3986         }
3987         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3988         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
3989     }
3991     sp_style_unref(query);
3994 void
3995 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
3997     sp_text_toolbox_selection_changed (selection, tbl);
4000 void
4001 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4003     sp_text_toolbox_selection_changed (NULL, tbl);
4006 void
4007 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4008                                 GObject             *tbl)
4010     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4011     GtkTreeModel *model = 0;
4012     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4013     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4014     GtkTreeIter   iter;
4015     char         *family = 0;
4017     (void)popdown;
4019     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4020     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4022     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4023         return;
4024     }
4026     gtk_tree_model_get (model, &iter, 0, &family, -1);
4028     if (g_object_get_data (G_OBJECT (selection), "block"))
4029     {
4030         gtk_entry_set_text (GTK_ENTRY (entry), family);
4031         return;
4032     }
4034     gtk_entry_set_text (GTK_ENTRY (entry), family);
4036     SPStyle *query =
4037         sp_style_new (SP_ACTIVE_DOCUMENT);
4039     int result_numbers =
4040         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4042     SPCSSAttr *css = sp_repr_css_attr_new ();
4043     sp_repr_css_set_property (css, "font-family", family);
4045     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4046     if (result_numbers == QUERY_STYLE_NOTHING)
4047     {
4048         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4049         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4050     }
4051     else
4052     {
4053         sp_desktop_set_style (desktop, css, true, true);
4054     }
4056     sp_style_unref(query);
4058     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4059                                    _("Text: Change font family"));
4060     sp_repr_css_attr_unref (css);
4061     free (family);
4062     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4064     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4067 void
4068 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4069                                        GObject      *tbl)
4071     const char *family = gtk_entry_get_text (entry);
4073     try {
4074         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4075         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4076         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4077         gtk_tree_selection_select_path (selection, path.gobj());
4078         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4079         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4080     } catch (...) {
4081         if (family && strlen (family))
4082         {
4083             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4084         }
4085     }
4088 void
4089 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4090                                    gpointer          data)
4092     if (g_object_get_data (G_OBJECT (button), "block")) return;
4093     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4094     int prop = GPOINTER_TO_INT(data);
4096     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4097     SPCSSAttr *css = sp_repr_css_attr_new ();
4099     switch (prop)
4100     {
4101         case 0:
4102         {
4103             sp_repr_css_set_property (css, "text-anchor", "start");
4104             sp_repr_css_set_property (css, "text-align", "start");
4105             break;
4106         }
4107         case 1:
4108         {
4109             sp_repr_css_set_property (css, "text-anchor", "middle");
4110             sp_repr_css_set_property (css, "text-align", "center");
4111             break;
4112         }
4114         case 2:
4115         {
4116             sp_repr_css_set_property (css, "text-anchor", "end");
4117             sp_repr_css_set_property (css, "text-align", "end");
4118             break;
4119         }
4121         case 3:
4122         {
4123             sp_repr_css_set_property (css, "text-anchor", "start");
4124             sp_repr_css_set_property (css, "text-align", "justify");
4125             break;
4126         }
4127     }
4129     SPStyle *query =
4130         sp_style_new (SP_ACTIVE_DOCUMENT);
4131     int result_numbers =
4132         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4134     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4135     if (result_numbers == QUERY_STYLE_NOTHING)
4136     {
4137         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4138     }
4140     sp_style_unref(query);
4142     sp_desktop_set_style (desktop, css, true, true);
4143     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4144                                    _("Text: Change alignment"));
4145     sp_repr_css_attr_unref (css);
4147     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4150 void
4151 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4152                                gpointer          data)
4154     if (g_object_get_data (G_OBJECT (button), "block")) return;
4156     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4157     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4158     int          prop       = GPOINTER_TO_INT(data);
4159     bool         active     = gtk_toggle_button_get_active (button);
4162     switch (prop)
4163     {
4164         case 0:
4165         {
4166             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4167             break;
4168         }
4170         case 1:
4171         {
4172             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4173             break;
4174         }
4175     }
4177     SPStyle *query =
4178         sp_style_new (SP_ACTIVE_DOCUMENT);
4179     int result_numbers =
4180         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4182     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4183     if (result_numbers == QUERY_STYLE_NOTHING)
4184     {
4185         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4186     }
4188     sp_style_unref(query);
4190     sp_desktop_set_style (desktop, css, true, true);
4191     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4192                                    _("Text: Change font style"));
4193     sp_repr_css_attr_unref (css);
4195     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4198 void
4199 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4200                                      gpointer         data)
4202     if (g_object_get_data (G_OBJECT (button), "block")) {
4203         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4204         return;
4205     }
4207     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4208     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4209     int          prop       = GPOINTER_TO_INT(data);
4211     switch (prop)
4212     {
4213         case 0:
4214         {
4215             sp_repr_css_set_property (css, "writing-mode", "lr");
4216             break;
4217         }
4219         case 1:
4220         {
4221             sp_repr_css_set_property (css, "writing-mode", "tb");
4222             break;
4223         }
4224     }
4226     SPStyle *query =
4227         sp_style_new (SP_ACTIVE_DOCUMENT);
4228     int result_numbers =
4229         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4231     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4232     if (result_numbers == QUERY_STYLE_NOTHING)
4233     {
4234         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4235     }
4237     sp_desktop_set_style (desktop, css, true, true);
4238     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4239                                    _("Text: Change orientation"));
4240     sp_repr_css_attr_unref (css);
4242     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4245 gboolean
4246 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4248     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4249     if (!desktop) return FALSE;
4251     switch (get_group0_keyval (event)) {
4252         case GDK_Escape: // defocus
4253             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4254             return TRUE; // I consumed the event
4255             break;
4256         case GDK_Return: // defocus
4257         case GDK_KP_Enter:
4258             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4259             return TRUE; // I consumed the event
4260             break;
4261     }
4262     return FALSE;
4265 gboolean
4266 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4268     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4269     if (!desktop) return FALSE;
4271     switch (get_group0_keyval (event)) {
4272         case GDK_Escape: // defocus
4273             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4274             sp_text_toolbox_selection_changed (NULL, tbl); // update
4275             return TRUE; // I consumed the event
4276             break;
4277     }
4278     return FALSE;
4281 gboolean
4282 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4284     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4285     if (!desktop) return FALSE;
4287     switch (get_group0_keyval (event)) {
4288         case GDK_KP_Enter:
4289         case GDK_Return:
4290         case GDK_Escape: // defocus
4291             gtk_widget_hide (w);
4292             visible = false;
4293             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4294             return TRUE; // I consumed the event
4295             break;
4296     }
4297     return FALSE;
4301 void
4302 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4303                                GObject     *tbl)
4305     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4307     if (g_object_get_data (tbl, "size-block")) return;
4309 #if GTK_CHECK_VERSION(2,6,0)
4310     char *text = gtk_combo_box_get_active_text (cbox);
4311 #else // GTK_CHECK_VERSION(2,6,0)
4312     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
4313     GtkTreeIter iter;
4314     char *text = NULL;
4316     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
4317         gtk_tree_model_get (model, &iter, 0, &text, -1);
4318 #endif // GTK_CHECK_VERSION(2,6,0)
4320     SPCSSAttr *css = sp_repr_css_attr_new ();
4321     sp_repr_css_set_property (css, "font-size", text);
4322     free (text);
4324     SPStyle *query =
4325         sp_style_new (SP_ACTIVE_DOCUMENT);
4326     int result_numbers =
4327         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4329     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4330     if (result_numbers == QUERY_STYLE_NOTHING)
4331     {
4332         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4333     }
4335     sp_style_unref(query);
4337     sp_desktop_set_style (desktop, css, true, true);
4338     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4339                                    _("Text: Change font size"));
4340     sp_repr_css_attr_unref (css);
4343     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4344         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4347 void
4348 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4349                                          GObject            *tbl)
4351     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4352     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4353     int x, y;
4355     if (!visible)
4356     {
4357         gdk_window_get_origin (widget->window, &x, &y);
4358         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4359         gtk_widget_show_all (popdown);
4361         gdk_pointer_grab (widget->window, TRUE,
4362                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4363                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4364                                         GDK_POINTER_MOTION_MASK),
4365                           NULL, NULL, GDK_CURRENT_TIME);
4367         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4369         visible = true;
4370     }
4371     else
4372     {
4373         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4374         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4375         gtk_widget_hide (popdown);
4376         visible = false;
4377     }
4380 gboolean
4381 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4382                                  GdkEventFocus    */*event*/,
4383                                  GObject          */*tbl*/)
4385     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4386     return FALSE;
4389 gboolean
4390 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4391                                    GdkEventFocus    */*event*/,
4392                                    GObject          */*tbl*/)
4394     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4396     gtk_widget_hide (popdown);
4397     visible = false;
4398     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4399     return TRUE;
4402 void
4403 cell_data_func  (GtkTreeViewColumn */*column*/,
4404                  GtkCellRenderer   *cell,
4405                  GtkTreeModel      *tree_model,
4406                  GtkTreeIter       *iter,
4407                  gpointer           /*data*/)
4409     char        *family,
4410         *family_escaped,
4411         *sample_escaped;
4413     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4415     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4417     family_escaped = g_markup_escape_text (family, -1);
4418     sample_escaped = g_markup_escape_text (sample, -1);
4420     std::stringstream markup;
4421     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4422     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4424     free (family);
4425     free (family_escaped);
4426     free (sample_escaped);
4429 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4430     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4431     if (completion) {
4432         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4433         g_object_unref (completion);
4434     }
4437 GtkWidget*
4438 sp_text_toolbox_new (SPDesktop *desktop)
4440     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4442     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4443     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4445     GtkTooltips *tt = gtk_tooltips_new();
4446     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4448     ////////////Family
4449     //Window
4450     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4451     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4453     //Entry
4454     GtkWidget           *entry = gtk_entry_new ();
4455     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4456     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4457     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4458     gtk_entry_completion_set_text_column (completion, 0);
4459     gtk_entry_completion_set_minimum_key_length (completion, 1);
4460     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4461     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4462     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4463     aux_toolbox_space (tbl, 1);
4464     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4465     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4467     //Button
4468     GtkWidget   *button = gtk_button_new ();
4469     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4470     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4472     //Popdown
4473     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4474     GtkWidget           *treeview = gtk_tree_view_new ();
4476     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4477     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4478     gtk_tree_view_column_pack_start (column, cell, FALSE);
4479     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4480     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4481     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4483     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4484     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4485 #if GTK_CHECK_VERSION(2,6,0)
4486     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4487 #endif // GTK_CHECK_VERSION(2,6,0)
4489     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4491     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4492     gtk_container_add (GTK_CONTAINER (sw), treeview);
4494     gtk_container_add (GTK_CONTAINER (window), sw);
4495     gtk_widget_set_size_request (window, 300, 450);
4497     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4498     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4499     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4501     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4503     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4504     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4506     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4507     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4509     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4510     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4511     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4512     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4513     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4515     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4516     aux_toolbox_space (tbl, 1);
4517     GtkWidget *box = gtk_event_box_new ();
4518     gtk_container_add (GTK_CONTAINER (box), image);
4519     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4520     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4521     GtkTooltips *tooltips = gtk_tooltips_new ();
4522     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4523     gtk_widget_hide (GTK_WIDGET (box));
4524     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4526     ////////////Size
4527     const char *sizes[] = {
4528         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4529         "16", "18", "20", "22", "24", "28",
4530         "32", "36", "40", "48", "56", "64", "72", "144"
4531     };
4533     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4534     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4535     gtk_widget_set_size_request (cbox, 80, -1);
4536     aux_toolbox_space (tbl, 1);
4537     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4538     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4539     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4540     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4542     //spacer
4543     aux_toolbox_space (tbl, 4);
4544     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4546     ////////////Text anchor
4547     GtkWidget *group   = gtk_radio_button_new (NULL);
4548     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4549     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4551     // left
4552     GtkWidget *rbutton = group;
4553     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4554     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4555     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4557     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4558     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4559     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4560     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4562     // center
4563     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4564     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4565     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4566     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4568     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4569     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4570     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4571     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4573     // right
4574     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4575     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4576     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4577     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4579     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4580     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4581     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4582     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4584     // fill
4585     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4586     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4587     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4588     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4590     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4591     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4592     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4593     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4595     aux_toolbox_space (tbl, 1);
4596     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4598     //spacer
4599     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4601     ////////////Text style
4602     row = gtk_hbox_new (FALSE, 4);
4604     // bold
4605     rbutton = gtk_toggle_button_new ();
4606     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4607     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4608     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4609     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4611     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4612     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4613     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4615     // italic
4616     rbutton = gtk_toggle_button_new ();
4617     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4618     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4619     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4620     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4622     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4623     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4624     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4626     aux_toolbox_space (tbl, 1);
4627     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4629     //spacer
4630     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4632     ////////////Text orientation
4633     group   = gtk_radio_button_new (NULL);
4634     row     = gtk_hbox_new (FALSE, 4);
4635     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4637     // horizontal
4638     rbutton = group;
4639     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4640     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4641     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4642     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4644     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4645     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4646     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4648     // vertical
4649     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4650     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4651     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4652     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4653     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4655     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4656     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4657     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4658     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4661     //watch selection
4662     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4664     sigc::connection *c_selection_changed =
4665         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4666                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4667     pool->add_connection ("selection-changed", c_selection_changed);
4669     sigc::connection *c_selection_modified =
4670         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4671                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4672     pool->add_connection ("selection-modified", c_selection_modified);
4674     sigc::connection *c_subselection_changed =
4675         new sigc::connection (desktop->connectToolSubselectionChanged
4676                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4677     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4679     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4682 #if 0
4683     // horizontal
4684     {
4685         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4686         GtkWidget *b = group = gtk_radio_button_new (NULL);
4687         gtk_container_add (GTK_CONTAINER (b), px);
4688         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4689         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4690         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4691         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4692     }
4694     // vertical
4695     {
4696         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4697         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4698         gtk_container_add (GTK_CONTAINER (b), px);
4699         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4700         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4701         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4702         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4703     }
4705     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4707     // letter spacing
4708     {
4709         {
4710             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4711             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4712             gtk_container_add (GTK_CONTAINER (hb), image);
4713             gtk_widget_show(image);
4714             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4715         }
4717         {
4718             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between letters"),
4719                                              "tools.text", "letter_spacing", 0.0,
4720                                              us, tbl, FALSE, NULL,
4721                                              -1000.0, 1000.0, 0.1, 0.1,
4722                                              sp_text_letter_changed, 0.1, 1);
4723             gtk_widget_set_size_request (hb, 45, 6);
4724             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4725         }
4726     }
4728     // line spacing
4729     {
4730         {
4731             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4732             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4733             gtk_container_add (GTK_CONTAINER (hb), image);
4734             gtk_widget_show(image);
4735             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4736         }
4738         {
4739             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between lines"),
4740                                              "tools.text", "line_spacing", 0,
4741                                              us, tbl, FALSE, NULL,
4742                                              -1000.0, 1000.0, 0.1, 0.1,
4743                                              sp_text_line_changed, 0.1, 1);
4744             gtk_widget_set_size_request (hb, 45, 0);
4745             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4746         }
4747     }
4749     {
4750         // horizontal kerning/vertical kerning units menu: create
4751         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4752         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4753         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4755         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4757         // horizontal kerning
4758         {
4759             {
4760                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4761                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4762                 gtk_container_add (GTK_CONTAINER (hb), image);
4763                 gtk_widget_show(image);
4764                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4765             }
4767             {
4768                 GtkWidget *hb = sp_tb_spinbutton((""), _("Horizontal kerning"),
4769                                                  "tools.text", "horizontal_kerning", 0,
4770                                                  us, tbl, FALSE, NULL,
4771                                                  -100.00, 100.00, 0.01, 0.1,
4772                                                  sp_text_horiz_kern_changed);
4773                 gtk_widget_set_size_request (hb, 45, 0);
4774                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4775             }
4776         }
4778         // vertical kerning
4779         {
4780             {
4781                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4782                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4783                 gtk_container_add (GTK_CONTAINER (hb), image);
4784                 gtk_widget_show(image);
4785                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4786             }
4788             {
4789                 GtkWidget *hb = sp_tb_spinbutton((""), _("Vertical kerning"),
4790                                                  "tools.text", "vertical_kerning", 0,
4791                                                  us, tbl, FALSE, NULL,
4792                                                  -100.00, 100.00, 0.01, 0.1,
4793                                                  sp_text_vert_kern_changed);
4794                 gtk_widget_set_size_request (hb, 45, 0);
4795                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4796             }
4797         }
4799         // add the units menu
4800         gtk_widget_show(us);
4801         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4802         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4803     }
4805     // letter rotation
4806     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4807     {
4808         {
4809             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4810             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4811             gtk_container_add (GTK_CONTAINER (hb), image);
4812             gtk_widget_show(image);
4813             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4814         }
4815         {
4816             GtkWidget *hb = sp_tb_spinbutton((""), _("Letter rotation"),
4817                                              "tools.text", "letter_rotation", 0,
4818                                              us, tbl, FALSE, NULL,
4819                                              -180.0, 180.0, 0.1, 0.1,
4820                                              sp_text_letter_rotation_changed, 0.1, 1);
4821             gtk_widget_set_size_request (hb, 45, 0);
4822             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4823         }
4824         // rotation degree label
4825         {
4826             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4827             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4828         }
4829     }
4831     // Remove Manual Kerns
4832     {
4833         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4834         GtkWidget *button = gtk_button_new ();
4835         gtk_container_add (GTK_CONTAINER (button), px);
4836         gtk_widget_show(button);
4837         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4838         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4839         gtk_widget_set_sensitive(button, TRUE);
4840         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4841     }
4842 #endif
4844     gtk_widget_show_all (tbl);
4845     return tbl;
4847 } // end of sp_text_toolbox_new()
4849 }//<unnamed> namespace
4852 //#########################
4853 //##      Connector      ##
4854 //#########################
4856 static void sp_connector_path_set_avoid(void)
4858     cc_selection_set_avoid(true);
4862 static void sp_connector_path_set_ignore(void)
4864     cc_selection_set_avoid(false);
4869 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4871     // quit if run by the _changed callbacks
4872     if (g_object_get_data( tbl, "freeze" )) {
4873         return;
4874     }
4876     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4877     SPDocument *doc = sp_desktop_document(desktop);
4879     if (!sp_document_get_undo_sensitive(doc))
4880     {
4881         return;
4882     }
4884     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4886     if ( repr->attribute("inkscape:connector-spacing") ) {
4887         gdouble priorValue = gtk_adjustment_get_value(adj);
4888         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4889         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4890             return;
4891         }
4892     } else if ( adj->value == defaultConnSpacing ) {
4893         return;
4894     }
4896     // in turn, prevent callbacks from responding
4897     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4899     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4900     SP_OBJECT(desktop->namedview)->updateRepr();
4902     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4903     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4904         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4905         NR::Matrix m = NR::identity();
4906         avoid_item_move(&m, item);
4907     }
4909     if (items) {
4910         g_slist_free(items);
4911     }
4913     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4914             _("Change connector spacing"));
4916     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4918     spinbutton_defocus(GTK_OBJECT(tbl));
4921 static void sp_connector_graph_layout(void)
4923     if (!SP_ACTIVE_DESKTOP) return;
4925     // hack for clones, see comment in align-and-distribute.cpp
4926     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4927     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4929     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4931     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4933     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4936 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4938     if ( gtk_toggle_action_get_active( act ) ) {
4939         prefs_set_string_attribute("tools.connector", "directedlayout",
4940                 "true");
4941     } else {
4942         prefs_set_string_attribute("tools.connector", "directedlayout",
4943                 "false");
4944     }
4947 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4949     if ( gtk_toggle_action_get_active( act ) ) {
4950         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4951                 "true");
4952     } else {
4953         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4954                 "false");
4955     }
4959 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4961     prefs_set_double_attribute("tools.connector", "length", adj->value);
4962     spinbutton_defocus(GTK_OBJECT(tbl));
4965 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4966                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
4967                                             bool /*is_interactive*/, gpointer data)
4969     GtkWidget *tbl = GTK_WIDGET(data);
4971     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4972         return;
4973     }
4974     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4975         return;
4976     }
4978     GtkAdjustment *adj = (GtkAdjustment*)
4979             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4980     gdouble spacing = defaultConnSpacing;
4981     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4983     gtk_adjustment_set_value(adj, spacing);
4987 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4988     NULL, /* child_added */
4989     NULL, /* child_removed */
4990     connector_tb_event_attr_changed,
4991     NULL, /* content_changed */
4992     NULL  /* order_changed */
4993 };
4996 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4998     {
4999         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5000                                           _("Avoid"),
5001                                           _("Make connectors avoid selected objects"),
5002                                           "connector_avoid",
5003                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5004         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5005         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5006     }
5008     {
5009         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5010                                           _("Ignore"),
5011                                           _("Make connectors ignore selected objects"),
5012                                           "connector_ignore",
5013                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5014         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5015         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5016     }
5018     EgeAdjustmentAction* eact = 0;
5020     // Spacing spinbox
5021     eact = create_adjustment_action( "ConnectorSpacingAction",
5022                                      _("Connector Spacing"), _("Spacing:"),
5023                                      _("The amount of space left around objects by auto-routing connectors"),
5024                                      "tools.connector", "spacing", defaultConnSpacing,
5025                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5026                                      0, 100, 1.0, 10.0,
5027                                      0, 0, 0,
5028                                      connector_spacing_changed, 1, 0 );
5029     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5031     // Graph (connector network) layout
5032     {
5033         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5034                                           _("Graph"),
5035                                           _("Nicely arrange selected connector network"),
5036                                           "graph_layout",
5037                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5038         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5039         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5040     }
5042     // Default connector length spinbox
5043     eact = create_adjustment_action( "ConnectorLengthAction",
5044                                      _("Connector Length"), _("Length:"),
5045                                      _("Ideal length for connectors when layout is applied"),
5046                                      "tools.connector", "length", 100,
5047                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5048                                      10, 1000, 10.0, 100.0,
5049                                      0, 0, 0,
5050                                      connector_length_changed, 1, 0 );
5051     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5054     // Directed edges toggle button
5055     {
5056         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5057                                                       _("Downwards"),
5058                                                       _("Make connectors with end-markers (arrows) point downwards"),
5059                                                       "directed_graph",
5060                                                       Inkscape::ICON_SIZE_DECORATION );
5061         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5063         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5064         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5065                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5067         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5068     }
5070     // Avoid overlaps toggle button
5071     {
5072         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5073                                                       _("Remove overlaps"),
5074                                                       _("Do not allow overlapping shapes"),
5075                                                       "remove_overlaps",
5076                                                       Inkscape::ICON_SIZE_DECORATION );
5077         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5079         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5080         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5081                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5083         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5084     }
5086     // Code to watch for changes to the connector-spacing attribute in
5087     // the XML.
5088     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5089     g_assert(repr != NULL);
5091     purge_repr_listener( holder, holder );
5093     if (repr) {
5094         g_object_set_data( holder, "repr", repr );
5095         Inkscape::GC::anchor(repr);
5096         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5097         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5098     }
5099 } // end of sp_connector_toolbox_prep()
5102 //#########################
5103 //##     Paintbucket     ##
5104 //#########################
5106 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5108     gint channels = ege_select_one_action_get_active( act );
5109     flood_channels_set_channels( channels );
5112 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5114     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5117 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5119     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5122 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5124     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5125     SPUnit const *unit = tracker->getActiveUnit();
5127     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5129     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5132 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5134     // FIXME: make defaults settable via Inkscape Options
5135     struct KeyValue {
5136         char const *key;
5137         double value;
5138     } const key_values[] = {
5139         {"threshold", 15},
5140         {"offset", 0.0}
5141     };
5143     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5144         KeyValue const &kv = key_values[i];
5145         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5146         if ( adj ) {
5147             gtk_adjustment_set_value(adj, kv.value);
5148         }
5149     }
5151     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5152     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5153     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5154     ege_select_one_action_set_active( autogap_action, 0 );
5157 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5159     EgeAdjustmentAction* eact = 0;
5161     {
5162         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5164         GList* items = 0;
5165         gint count = 0;
5166         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5167         {
5168             GtkTreeIter iter;
5169             gtk_list_store_append( model, &iter );
5170             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5171             count++;
5172         }
5173         g_list_free( items );
5174         items = 0;
5175         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5176         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5177         ege_select_one_action_set_appearance( act1, "compact" );
5178         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5179         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5180         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5181         g_object_set_data( holder, "channels_action", act1 );
5182     }
5184     // Spacing spinbox
5185     {
5186         eact = create_adjustment_action(
5187             "ThresholdAction",
5188             _("Fill Threshold"), _("Threshold:"),
5189             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5190             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5191             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5192             0, 0, 0,
5193             paintbucket_threshold_changed, 1, 0 );
5195         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5196     }
5198     // Create the units menu.
5199     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5200     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5201     g_object_set_data( holder, "tracker", tracker );
5202     {
5203         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5204         gtk_action_group_add_action( mainActions, act );
5205     }
5207     // Offset spinbox
5208     {
5209         eact = create_adjustment_action(
5210             "OffsetAction",
5211             _("Grow/shrink by"), _("Grow/shrink by:"),
5212             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5213             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5214             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5215             0, 0, 0,
5216             paintbucket_offset_changed, 1, 2);
5217         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5219         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5220     }
5222     /* Auto Gap */
5223     {
5224         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5226         GList* items = 0;
5227         gint count = 0;
5228         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5229         {
5230             GtkTreeIter iter;
5231             gtk_list_store_append( model, &iter );
5232             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5233             count++;
5234         }
5235         g_list_free( items );
5236         items = 0;
5237         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5238         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5239         ege_select_one_action_set_appearance( act2, "compact" );
5240         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5241         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5242         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5243         g_object_set_data( holder, "autogap_action", act2 );
5244     }
5246     /* Reset */
5247     {
5248         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5249                                           _("Defaults"),
5250                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5251                                           GTK_STOCK_CLEAR );
5252         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5253         gtk_action_group_add_action( mainActions, act );
5254         gtk_action_set_sensitive( act, TRUE );
5255     }
5259 /*
5260   Local Variables:
5261   mode:c++
5262   c-file-style:"stroustrup"
5263   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5264   indent-tabs-mode:nil
5265   fill-column:99
5266   End:
5267 */
5268 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :