Code

8ac019cb7921b5b829de8ec2d1ec215ec7f10828
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "sp-clippath.h"
78 #include "sp-mask.h"
79 #include "style.h"
80 #include "selection.h"
81 #include "selection-chemistry.h"
82 #include "document-private.h"
83 #include "desktop-style.h"
84 #include "../libnrtype/font-lister.h"
85 #include "../libnrtype/font-instance.h"
86 #include "../connection-pool.h"
87 #include "../prefs-utils.h"
88 #include "../inkscape-stock.h"
89 #include "icon.h"
90 #include "graphlayout/graphlayout.h"
92 #include "mod360.h"
94 #include "toolbox.h"
96 #include "flood-context.h"
98 #include "ink-action.h"
99 #include "ege-adjustment-action.h"
100 #include "ege-output-action.h"
101 #include "ege-select-one-action.h"
102 #include "helper/unit-tracker.h"
104 #include "svg/css-ostringstream.h"
106 using Inkscape::UnitTracker;
108 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
109 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
111 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
124 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
130 static struct {
131     gchar const *type_name;
132     gchar const *data_name;
133     sp_verb_t verb;
134     sp_verb_t doubleclick_verb;
135 } const tools[] = {
136     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
137     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
138     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
139     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
140     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
141     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
142     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
143     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
144     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
145     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
146     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
147     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
148     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
149     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
150     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
151     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
152     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
153     { NULL, NULL, 0, 0 }
154 };
156 static struct {
157     gchar const *type_name;
158     gchar const *data_name;
159     GtkWidget *(*create_func)(SPDesktop *desktop);
160     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
161     gchar const *ui_name;
162     gint swatch_verb_id;
163     gchar const *swatch_tool;
164     gchar const *swatch_tip;
165 } const aux_toolboxes[] = {
166     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
167       SP_VERB_INVALID, 0, 0},
168     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
169       SP_VERB_INVALID, 0, 0},
170     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
171       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
172     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
173       SP_VERB_INVALID, 0, 0},
174     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
175       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
176     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
177       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
178     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
179       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
180     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
181       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
182     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
183       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
184     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
185       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
186     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
187       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
188     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
189       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
190     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
191       SP_VERB_INVALID, 0, 0},
192     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
193       SP_VERB_INVALID, 0, 0},
194     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
195       SP_VERB_INVALID, 0, 0},
196     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
197       SP_VERB_INVALID, 0, 0},
198     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
199       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
200     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
201 };
204 static gchar const * ui_descr =
205         "<ui>"
206         "  <toolbar name='SelectToolbar'>"
207         "    <toolitem action='EditSelectAll' />"
208         "    <toolitem action='EditSelectAllInAllLayers' />"
209         "    <toolitem action='EditDeselect' />"
210         "    <separator />"
211         "    <toolitem action='ObjectRotate90CCW' />"
212         "    <toolitem action='ObjectRotate90' />"
213         "    <toolitem action='ObjectFlipHorizontally' />"
214         "    <toolitem action='ObjectFlipVertically' />"
215         "    <separator />"
216         "    <toolitem action='SelectionToBack' />"
217         "    <toolitem action='SelectionLower' />"
218         "    <toolitem action='SelectionRaise' />"
219         "    <toolitem action='SelectionToFront' />"
220         "    <separator />"
221         "    <toolitem action='XAction' />"
222         "    <toolitem action='YAction' />"
223         "    <toolitem action='WidthAction' />"
224         "    <toolitem action='LockAction' />"
225         "    <toolitem action='HeightAction' />"
226         "    <toolitem action='UnitsAction' />"
227         "    <separator />"
228         "    <toolitem action='transform_affect_label' />"
229         "    <toolitem action='transform_stroke' />"
230         "    <toolitem action='transform_corners' />"
231         "    <toolitem action='transform_gradient' />"
232         "    <toolitem action='transform_pattern' />"
233         "  </toolbar>"
235         "  <toolbar name='NodeToolbar'>"
236         "    <toolitem action='NodeInsertAction' />"
237         "    <toolitem action='NodeDeleteAction' />"
238         "    <separator />"
239         "    <toolitem action='NodeJoinAction' />"
240         "    <toolitem action='NodeJoinSegmentAction' />"
241         "    <toolitem action='NodeDeleteSegmentAction' />"
242         "    <toolitem action='NodeBreakAction' />"
243         "    <separator />"
244         "    <toolitem action='NodeCuspAction' />"
245         "    <toolitem action='NodeSmoothAction' />"
246         "    <toolitem action='NodeSymmetricAction' />"
247         "    <separator />"
248         "    <toolitem action='NodeLineAction' />"
249         "    <toolitem action='NodeCurveAction' />"
250         "    <separator />"
251         "    <toolitem action='ObjectToPath' />"
252         "    <toolitem action='StrokeToPath' />"
253         "    <separator />"
254         "    <toolitem action='NodeXAction' />"
255         "    <toolitem action='NodeYAction' />"
256         "    <toolitem action='NodeUnitsAction' />"
257         "    <separator />"
258         "    <toolitem action='ObjectEditClipPathAction' />"
259         "    <toolitem action='ObjectEditMaskPathAction' />"
260         "    <toolitem action='EditNextLPEParameterAction' />"
261         "    <separator />"
262         "    <toolitem action='NodesShowHandlesAction' />"
263         "    <toolitem action='NodesShowHelperpath' />"
264         "  </toolbar>"
266         "  <toolbar name='TweakToolbar'>"
267         "    <toolitem action='TweakWidthAction' />"
268         "    <separator />"
269         "    <toolitem action='TweakForceAction' />"
270         "    <toolitem action='TweakPressureAction' />"
271         "    <separator />"
272         "    <toolitem action='TweakModeAction' />"
273         "    <separator />"
274         "    <toolitem action='TweakFidelityAction' />"
275         "    <separator />"
276         "    <toolitem action='TweakChannelsLabel' />"
277         "    <toolitem action='TweakDoH' />"
278         "    <toolitem action='TweakDoS' />"
279         "    <toolitem action='TweakDoL' />"
280         "    <toolitem action='TweakDoO' />"
281         "  </toolbar>"
283         "  <toolbar name='ZoomToolbar'>"
284         "    <toolitem action='ZoomIn' />"
285         "    <toolitem action='ZoomOut' />"
286         "    <separator />"
287         "    <toolitem action='Zoom1:0' />"
288         "    <toolitem action='Zoom1:2' />"
289         "    <toolitem action='Zoom2:1' />"
290         "    <separator />"
291         "    <toolitem action='ZoomSelection' />"
292         "    <toolitem action='ZoomDrawing' />"
293         "    <toolitem action='ZoomPage' />"
294         "    <toolitem action='ZoomPageWidth' />"
295         "    <separator />"
296         "    <toolitem action='ZoomPrev' />"
297         "    <toolitem action='ZoomNext' />"
298         "  </toolbar>"
300         "  <toolbar name='StarToolbar'>"
301         "    <separator />"
302         "    <toolitem action='StarStateAction' />"
303         "    <separator />"
304         "    <toolitem action='FlatAction' />"
305         "    <separator />"
306         "    <toolitem action='MagnitudeAction' />"
307         "    <toolitem action='SpokeAction' />"
308         "    <toolitem action='RoundednessAction' />"
309         "    <toolitem action='RandomizationAction' />"
310         "    <separator />"
311         "    <toolitem action='StarResetAction' />"
312         "  </toolbar>"
314         "  <toolbar name='RectToolbar'>"
315         "    <toolitem action='RectStateAction' />"
316         "    <toolitem action='RectWidthAction' />"
317         "    <toolitem action='RectHeightAction' />"
318         "    <toolitem action='RadiusXAction' />"
319         "    <toolitem action='RadiusYAction' />"
320         "    <toolitem action='RectUnitsAction' />"
321         "    <separator />"
322         "    <toolitem action='RectResetAction' />"
323         "  </toolbar>"
325         "  <toolbar name='3DBoxToolbar'>"
326         "    <toolitem action='3DBoxAngleXAction' />"
327         "    <toolitem action='3DBoxVPXStateAction' />"
328         "    <separator />"
329         "    <toolitem action='3DBoxAngleYAction' />"
330         "    <toolitem action='3DBoxVPYStateAction' />"
331         "    <separator />"
332         "    <toolitem action='3DBoxAngleZAction' />"
333         "    <toolitem action='3DBoxVPZStateAction' />"
334         "  </toolbar>"
336         "  <toolbar name='SpiralToolbar'>"
337         "    <toolitem action='SpiralStateAction' />"
338         "    <toolitem action='SpiralRevolutionAction' />"
339         "    <toolitem action='SpiralExpansionAction' />"
340         "    <toolitem action='SpiralT0Action' />"
341         "    <separator />"
342         "    <toolitem action='SpiralResetAction' />"
343         "  </toolbar>"
345         "  <toolbar name='PenToolbar'>"
346         "  </toolbar>"
348         "  <toolbar name='PencilToolbar'>"
349         "  </toolbar>"
351         "  <toolbar name='CalligraphyToolbar'>"
352         "    <separator />"
353         "    <toolitem action='SetProfileAction'/>"
354         "    <separator />"
355         "    <toolitem action='CalligraphyWidthAction' />"
356         "    <toolitem action='PressureAction' />"
357         "    <toolitem action='TraceAction' />"
358         "    <toolitem action='ThinningAction' />"
359         "    <separator />"
360         "    <toolitem action='AngleAction' />"
361         "    <toolitem action='TiltAction' />"
362         "    <toolitem action='FixationAction' />"
363         "    <separator />"
364         "    <toolitem action='CapRoundingAction' />"
365         "    <separator />"
366         "    <toolitem action='TremorAction' />"
367         "    <toolitem action='WiggleAction' />"
368         "    <toolitem action='MassAction' />"
369         "    <separator />"
370         "  </toolbar>"
372         "  <toolbar name='ArcToolbar'>"
373         "    <toolitem action='ArcStateAction' />"
374         "    <separator />"
375         "    <toolitem action='ArcStartAction' />"
376         "    <toolitem action='ArcEndAction' />"
377         "    <separator />"
378         "    <toolitem action='ArcOpenAction' />"
379         "    <separator />"
380         "    <toolitem action='ArcResetAction' />"
381         "    <separator />"
382         "  </toolbar>"
384         "  <toolbar name='PaintbucketToolbar'>"
385         "    <toolitem action='ChannelsAction' />"
386         "    <separator />"
387         "    <toolitem action='ThresholdAction' />"
388         "    <separator />"
389         "    <toolitem action='OffsetAction' />"
390         "    <toolitem action='PaintbucketUnitsAction' />"
391         "    <separator />"
392         "    <toolitem action='AutoGapAction' />"
393         "    <separator />"
394         "    <toolitem action='PaintbucketResetAction' />"
395         "  </toolbar>"
397         "  <toolbar name='DropperToolbar'>"
398         "    <toolitem action='DropperOpacityAction' />"
399         "    <toolitem action='DropperPickAlphaAction' />"
400         "    <toolitem action='DropperSetAlphaAction' />"
401         "  </toolbar>"
403         "  <toolbar name='ConnectorToolbar'>"
404         "    <toolitem action='ConnectorAvoidAction' />"
405         "    <toolitem action='ConnectorIgnoreAction' />"
406         "    <toolitem action='ConnectorSpacingAction' />"
407         "    <toolitem action='ConnectorGraphAction' />"
408         "    <toolitem action='ConnectorLengthAction' />"
409         "    <toolitem action='ConnectorDirectedAction' />"
410         "    <toolitem action='ConnectorOverlapAction' />"
411         "  </toolbar>"
413         "</ui>"
416 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
418 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
420 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
421 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
423 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
424 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
426 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
427 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
429 /* Global text entry widgets necessary for update */
430 /* GtkWidget *dropper_rgb_entry,
431           *dropper_opacity_entry ; */
432 // should be made a private member once this is converted to class
434 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
435     connection->disconnect();
436     delete connection;
439 static void purge_repr_listener( GObject* obj, GObject* tbl )
441     (void)obj;
442     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
443     if (oldrepr) { // remove old listener
444         sp_repr_remove_listener_by_data(oldrepr, tbl);
445         Inkscape::GC::release(oldrepr);
446         oldrepr = 0;
447         g_object_set_data( tbl, "repr", NULL );
448     }
451 GtkWidget *
452 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
453                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
454                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
456     SPAction *action = verb->get_action(view);
457     if (!action) return NULL;
459     SPAction *doubleclick_action;
460     if (doubleclick_verb)
461         doubleclick_action = doubleclick_verb->get_action(view);
462     else
463         doubleclick_action = NULL;
465     /* fixme: Handle sensitive/unsensitive */
466     /* fixme: Implement sp_button_new_from_action */
467     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
468     gtk_widget_show(b);
469     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
471     return b;
474 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
475                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
477     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
480 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
481                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
483     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
487 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
489     SPAction* targetAction = SP_ACTION(user_data);
490     if ( targetAction ) {
491         sp_action_perform( targetAction, NULL );
492     }
495 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
497     if ( data ) {
498         GtkAction* act = GTK_ACTION(data);
499         gtk_action_set_sensitive( act, sensitive );
500     }
503 static SPActionEventVector action_event_vector = {
504     {NULL},
505     NULL,
506     NULL,
507     sp_action_action_set_sensitive,
508     NULL,
509     NULL
510 };
512 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
514     GtkAction* act = 0;
516     SPAction* targetAction = verb->get_action(view);
517     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
518     act = GTK_ACTION(inky);
519     gtk_action_set_sensitive( act, targetAction->sensitive );
521     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
523     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
524     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
526     return act;
529 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
531     Inkscape::UI::View::View *view = desktop;
532     gint verbsToUse[] = {
533         // disabled until we have icons for them:
534         //find
535         //SP_VERB_EDIT_TILE,
536         //SP_VERB_EDIT_UNTILE,
537         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
538         SP_VERB_DIALOG_DISPLAY,
539         SP_VERB_DIALOG_FILL_STROKE,
540         SP_VERB_DIALOG_NAMEDVIEW,
541         SP_VERB_DIALOG_TEXT,
542         SP_VERB_DIALOG_XML_EDITOR,
543         SP_VERB_EDIT_CLONE,
544         SP_VERB_EDIT_COPY,
545         SP_VERB_EDIT_CUT,
546         SP_VERB_EDIT_DUPLICATE,
547         SP_VERB_EDIT_PASTE,
548         SP_VERB_EDIT_REDO,
549         SP_VERB_EDIT_UNDO,
550         SP_VERB_EDIT_UNLINK_CLONE,
551         SP_VERB_FILE_EXPORT,
552         SP_VERB_FILE_IMPORT,
553         SP_VERB_FILE_NEW,
554         SP_VERB_FILE_OPEN,
555         SP_VERB_FILE_PRINT,
556         SP_VERB_FILE_SAVE,
557         SP_VERB_OBJECT_TO_CURVE,
558         SP_VERB_SELECTION_GROUP,
559         SP_VERB_SELECTION_OUTLINE,
560         SP_VERB_SELECTION_UNGROUP,
561         SP_VERB_ZOOM_1_1,
562         SP_VERB_ZOOM_1_2,
563         SP_VERB_ZOOM_2_1,
564         SP_VERB_ZOOM_DRAWING,
565         SP_VERB_ZOOM_IN,
566         SP_VERB_ZOOM_NEXT,
567         SP_VERB_ZOOM_OUT,
568         SP_VERB_ZOOM_PAGE,
569         SP_VERB_ZOOM_PAGE_WIDTH,
570         SP_VERB_ZOOM_PREV,
571         SP_VERB_ZOOM_SELECTION,
572     };
574     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
575     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
577     static std::map<SPDesktop*, GtkActionGroup*> groups;
578     GtkActionGroup* mainActions = 0;
579     if ( groups.find(desktop) != groups.end() ) {
580         mainActions = groups[desktop];
581     }
583     if ( !mainActions ) {
584         mainActions = gtk_action_group_new("main");
585         groups[desktop] = mainActions;
586     }
588     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
589         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
590         if ( verb ) {
591             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
592                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
593                 gtk_action_group_add_action( mainActions, act );
594             }
595         }
596     }
598     return mainActions;
602 GtkWidget *
603 sp_tool_toolbox_new()
605     GtkTooltips *tt = gtk_tooltips_new();
606     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
608     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
609     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
611     gtk_widget_set_sensitive(tb, FALSE);
613     GtkWidget *hb = gtk_handle_box_new();
614     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
615     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
616     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
618     gtk_container_add(GTK_CONTAINER(hb), tb);
619     gtk_widget_show(GTK_WIDGET(tb));
621     sigc::connection* conn = new sigc::connection;
622     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
624     return hb;
627 static void
628 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
630     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
631     gtk_widget_queue_resize(child);
634 static void
635 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
637     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
638     gtk_widget_queue_resize(child);
641 GtkWidget *
642 sp_aux_toolbox_new()
644     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
646     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
648     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
650     gtk_widget_set_sensitive(tb, FALSE);
652     GtkWidget *hb = gtk_handle_box_new();
653     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
654     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
655     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
657     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
658     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
660     gtk_container_add(GTK_CONTAINER(hb), tb);
661     gtk_widget_show(GTK_WIDGET(tb));
663     sigc::connection* conn = new sigc::connection;
664     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
666     return hb;
669 //####################################
670 //# Commands Bar
671 //####################################
673 GtkWidget *
674 sp_commands_toolbox_new()
676     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
678     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
680     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
681     gtk_widget_set_sensitive(tb, FALSE);
683     GtkWidget *hb = gtk_handle_box_new();
684     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
685     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
686     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
688     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
689     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
691     gtk_container_add(GTK_CONTAINER(hb), tb);
692     gtk_widget_show(GTK_WIDGET(tb));
694     sigc::connection* conn = new sigc::connection;
695     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
697     return hb;
700 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
701                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
702                                                        gchar const *path, gchar const *data, gdouble def,
703                                                        GtkWidget *focusTarget,
704                                                        GtkWidget *us,
705                                                        GObject *dataKludge,
706                                                        gboolean altx, gchar const *altx_mark,
707                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
708                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
709                                                        void (*callback)(GtkAdjustment *, GObject *),
710                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
712     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
713                                                              lower, upper, step, page, page ) );
714     if (us) {
715         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
716     }
718     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
720     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
721     if ( shortLabel ) {
722         g_object_set( act, "short_label", shortLabel, NULL );
723     }
725     if ( (descrCount > 0) && descrLabels && descrValues ) {
726         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
727     }
729     if ( focusTarget ) {
730         ege_adjustment_action_set_focuswidget( act, focusTarget );
731     }
733     if ( altx && altx_mark ) {
734         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
735     }
737     if ( dataKludge ) {
738         g_object_set_data( dataKludge, data, adj );
739     }
741     // Using a cast just to make sure we pass in the right kind of function pointer
742     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
744     return act;
748 //####################################
749 //# node editing callbacks
750 //####################################
752 /**
753  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
754  */
755 static ShapeEditor *get_current_shape_editor()
757     if (!SP_ACTIVE_DESKTOP) {
758         return NULL;
759     }
761     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
763     if (!SP_IS_NODE_CONTEXT(event_context)) {
764         return NULL;
765     }
767     return SP_NODE_CONTEXT(event_context)->shape_editor;
771 void
772 sp_node_path_edit_add(void)
774     ShapeEditor *shape_editor = get_current_shape_editor();
775     if (shape_editor) shape_editor->add_node();
778 void
779 sp_node_path_edit_delete(void)
781     ShapeEditor *shape_editor = get_current_shape_editor();
782     if (shape_editor) shape_editor->delete_nodes();
785 void
786 sp_node_path_edit_delete_segment(void)
788     ShapeEditor *shape_editor = get_current_shape_editor();
789     if (shape_editor) shape_editor->delete_segment();
792 void
793 sp_node_path_edit_break(void)
795     ShapeEditor *shape_editor = get_current_shape_editor();
796     if (shape_editor) shape_editor->break_at_nodes();
799 void
800 sp_node_path_edit_join(void)
802     ShapeEditor *shape_editor = get_current_shape_editor();
803     if (shape_editor) shape_editor->join_nodes();
806 void
807 sp_node_path_edit_join_segment(void)
809     ShapeEditor *shape_editor = get_current_shape_editor();
810     if (shape_editor) shape_editor->join_segments();
813 void
814 sp_node_path_edit_toline(void)
816     ShapeEditor *shape_editor = get_current_shape_editor();
817     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
820 void
821 sp_node_path_edit_tocurve(void)
823     ShapeEditor *shape_editor = get_current_shape_editor();
824     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
827 void
828 sp_node_path_edit_cusp(void)
830     ShapeEditor *shape_editor = get_current_shape_editor();
831     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
834 void
835 sp_node_path_edit_smooth(void)
837     ShapeEditor *shape_editor = get_current_shape_editor();
838     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
841 void
842 sp_node_path_edit_symmetrical(void)
844     ShapeEditor *shape_editor = get_current_shape_editor();
845     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
848 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
849     bool show = gtk_toggle_action_get_active( act );
850     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
851     ShapeEditor *shape_editor = get_current_shape_editor();
852     if (shape_editor) shape_editor->show_handles(show);
855 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
856     bool show = gtk_toggle_action_get_active( act );
857     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
858     ShapeEditor *shape_editor = get_current_shape_editor();
859     if (shape_editor) shape_editor->show_helperpath(show);
862 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
863     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
866 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
867     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
870 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
871     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
874 /* is called when the node selection is modified */
875 static void
876 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
878     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
879     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
880     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
881     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
883     // quit if run by the attr_changed listener
884     if (g_object_get_data( tbl, "freeze" )) {
885         return;
886     }
888     // in turn, prevent listener from responding
889     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
891     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
892     SPUnit const *unit = tracker->getActiveUnit();
894     ShapeEditor *shape_editor = get_current_shape_editor();
895     if (shape_editor && shape_editor->has_nodepath()) {
896         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
897         int n_selected = 0;
898         if (nodepath) {
899             n_selected = nodepath->numSelected();
900         }
902         if (n_selected == 0) {
903             gtk_action_set_sensitive(xact, FALSE);
904             gtk_action_set_sensitive(yact, FALSE);
905         } else {
906             gtk_action_set_sensitive(xact, TRUE);
907             gtk_action_set_sensitive(yact, TRUE);
908             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
909             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
911             if (n_selected == 1) {
912                 NR::Point sel_node = nodepath->singleSelectedCoords();
913                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
914                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
915                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
916                 }
917             } else {
918                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
919                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
920                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
921                     /* Note: Currently x and y will always have a value, even if the coordinates of the
922                        selected nodes don't coincide (in this case we use the coordinates of the center
923                        of the bounding box). So the entries are never set to zero. */
924                     // FIXME: Maybe we should clear the entry if several nodes are selected
925                     //        instead of providing a kind of average value
926                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
927                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
928                 }
929             }
930         }
931     } else {
932         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
933         gtk_action_set_sensitive(xact, FALSE);
934         gtk_action_set_sensitive(yact, FALSE);
935     }
937     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
940 static void
941 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
943     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
945     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
946     SPUnit const *unit = tracker->getActiveUnit();
948     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
949         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
950     }
952     // quit if run by the attr_changed listener
953     if (g_object_get_data( tbl, "freeze" )) {
954         return;
955     }
957     // in turn, prevent listener from responding
958     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
960     ShapeEditor *shape_editor = get_current_shape_editor();
961     if (shape_editor && shape_editor->has_nodepath()) {
962         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
963         if (!strcmp(value_name, "x")) {
964             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
965         }
966         if (!strcmp(value_name, "y")) {
967             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
968         }
969     }
971     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
974 static void
975 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
977     sp_node_path_value_changed(adj, tbl, "x");
980 static void
981 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
983     sp_node_path_value_changed(adj, tbl, "y");
986 void
987 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
989     {
990     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
991     SPItem *item = selection->singleItem();
992     if (item && SP_IS_SHAPE(item)) {
993        LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(item));
994        if (lpeobj) {
995            gtk_action_set_sensitive(w, TRUE);
996        } else {
997            gtk_action_set_sensitive(w, FALSE);
998        }
999     } else {
1000        gtk_action_set_sensitive(w, FALSE);
1001     }
1002     }
1004     {
1005     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1006     SPItem *item = selection->singleItem();
1007     if (item && item->clip_ref && item->clip_ref->getObject()) {
1008        gtk_action_set_sensitive(w, TRUE);
1009     } else {
1010        gtk_action_set_sensitive(w, FALSE);
1011     }
1012     }
1014     {
1015     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1016     SPItem *item = selection->singleItem();
1017     if (item && item->mask_ref && item->mask_ref->getObject()) {
1018        gtk_action_set_sensitive(w, TRUE);
1019     } else {
1020        gtk_action_set_sensitive(w, FALSE);
1021     }
1022     }
1025 void
1026 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1028     sp_node_toolbox_sel_changed (selection, tbl);
1033 //################################
1034 //##    Node Editing Toolbox    ##
1035 //################################
1037 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1039     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1040     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1041     g_object_set_data( holder, "tracker", tracker );
1043     {
1044         InkAction* inky = ink_action_new( "NodeInsertAction",
1045                                           _("Insert node"),
1046                                           _("Insert new nodes into selected segments"),
1047                                           "node_insert",
1048                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1049         g_object_set( inky, "short_label", _("Insert"), NULL );
1050         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1051         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1052     }
1054     {
1055         InkAction* inky = ink_action_new( "NodeDeleteAction",
1056                                           _("Delete node"),
1057                                           _("Delete selected nodes"),
1058                                           "node_delete",
1059                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1060         g_object_set( inky, "short_label", _("Delete"), NULL );
1061         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1062         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1063     }
1065     {
1066         InkAction* inky = ink_action_new( "NodeJoinAction",
1067                                           _("Join endnodes"),
1068                                           _("Join selected endnodes"),
1069                                           "node_join",
1070                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1071         g_object_set( inky, "short_label", _("Join"), NULL );
1072         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1073         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1074     }
1076     {
1077         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1078                                           _("Join Segment"),
1079                                           _("Join selected endnodes with a new segment"),
1080                                           "node_join_segment",
1081                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1082         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1083         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1084     }
1086     {
1087         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1088                                           _("Delete Segment"),
1089                                           _("Split path between two non-endpoint nodes"),
1090                                           "node_delete_segment",
1091                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1092         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1093         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1094     }
1096     {
1097         InkAction* inky = ink_action_new( "NodeBreakAction",
1098                                           _("Node Break"),
1099                                           _("Break path at selected nodes"),
1100                                           "node_break",
1101                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1102         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1103         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1104     }
1106     {
1107         InkAction* inky = ink_action_new( "NodeCuspAction",
1108                                           _("Node Cusp"),
1109                                           _("Make selected nodes corner"),
1110                                           "node_cusp",
1111                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1112         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1113         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1114     }
1116     {
1117         InkAction* inky = ink_action_new( "NodeSmoothAction",
1118                                           _("Node Smooth"),
1119                                           _("Make selected nodes smooth"),
1120                                           "node_smooth",
1121                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1122         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1123         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1124     }
1126     {
1127         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1128                                           _("Node Symmetric"),
1129                                           _("Make selected nodes symmetric"),
1130                                           "node_symmetric",
1131                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1132         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1133         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1134     }
1136     {
1137         InkAction* inky = ink_action_new( "NodeLineAction",
1138                                           _("Node Line"),
1139                                           _("Make selected segments lines"),
1140                                           "node_line",
1141                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1142         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1143         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1144     }
1146     {
1147         InkAction* inky = ink_action_new( "NodeCurveAction",
1148                                           _("Node Curve"),
1149                                           _("Make selected segments curves"),
1150                                           "node_curve",
1151                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1152         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1153         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1154     }
1156     {
1157         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1158                                                       _("Show Handles"),
1159                                                       _("Show the Bezier handles of selected nodes"),
1160                                                       "nodes_show_handles",
1161                                                       Inkscape::ICON_SIZE_DECORATION );
1162         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1163         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1164         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1165     }
1167     {
1168         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1169                                                       _("Show Outline"),
1170                                                       _("Show the outline of the path"),
1171                                                       "nodes_show_helperpath",
1172                                                       Inkscape::ICON_SIZE_DECORATION );
1173         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1174         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1175         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1176     }
1178     {
1179         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1180                                           _("Next path effect parameter"),
1181                                           _("Show next path effect parameter for editing"),
1182                                           "edit_next_parameter",
1183                                           Inkscape::ICON_SIZE_DECORATION );
1184         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1185         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1186         g_object_set_data( holder, "nodes_lpeedit", inky);
1187     }
1189     {
1190         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1191                                           _("Edit clipping path"),
1192                                           _("Edit the clipping path of the object"),
1193                                           "nodeedit-clippath",
1194                                           Inkscape::ICON_SIZE_DECORATION );
1195         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1196         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1197         g_object_set_data( holder, "nodes_clippathedit", inky);
1198     }
1200     {
1201         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1202                                           _("Edit mask path"),
1203                                           _("Edit the mask of the object"),
1204                                           "nodeedit-mask",
1205                                           Inkscape::ICON_SIZE_DECORATION );
1206         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1207         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1208         g_object_set_data( holder, "nodes_maskedit", inky);
1209     }
1211     /* X coord of selected node(s) */
1212     {
1213         EgeAdjustmentAction* eact = 0;
1214         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1215         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1216         eact = create_adjustment_action( "NodeXAction",
1217                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1218                                          "tools.nodes", "Xcoord", 0,
1219                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1220                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1221                                          labels, values, G_N_ELEMENTS(labels),
1222                                          sp_node_path_x_value_changed );
1223         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1224         g_object_set_data( holder, "nodes_x_action", eact );
1225         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1226         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1227     }
1229     /* Y coord of selected node(s) */
1230     {
1231         EgeAdjustmentAction* eact = 0;
1232         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1233         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1234         eact = create_adjustment_action( "NodeYAction",
1235                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1236                                          "tools.nodes", "Ycoord", 0,
1237                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1238                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1239                                          labels, values, G_N_ELEMENTS(labels),
1240                                          sp_node_path_y_value_changed );
1241         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1242         g_object_set_data( holder, "nodes_y_action", eact );
1243         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1244         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1245     }
1247     // add the units menu
1248     {
1249         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1250         gtk_action_group_add_action( mainActions, act );
1251     }
1254     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1256     //watch selection
1257     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1259     sigc::connection *c_selection_changed =
1260         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1261                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1262     pool->add_connection ("selection-changed", c_selection_changed);
1264     sigc::connection *c_selection_modified =
1265         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1266                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1267     pool->add_connection ("selection-modified", c_selection_modified);
1269     sigc::connection *c_subselection_changed =
1270         new sigc::connection (desktop->connectToolSubselectionChanged
1271                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1272     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1274     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1276     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1277 } // end of sp_node_toolbox_prep()
1280 //########################
1281 //##    Zoom Toolbox    ##
1282 //########################
1284 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1286     // no custom GtkAction setup needed
1287 } // end of sp_zoom_toolbox_prep()
1289 void
1290 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1292     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")));
1296 void
1297 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1299     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")));
1302 void
1303 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1305     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")));
1308 static void
1309 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1311     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1312     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1314     if (old_desktop) {
1315         GList *children, *iter;
1317         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1318         for ( iter = children ; iter ; iter = iter->next ) {
1319             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1320         }
1321         g_list_free(children);
1322     }
1324     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1326     if (desktop) {
1327         gtk_widget_set_sensitive(toolbox, TRUE);
1328         setup_func(toolbox, desktop);
1329         update_func(desktop, desktop->event_context, toolbox);
1330         *conn = desktop->connectEventContextChanged
1331             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1332     } else {
1333         gtk_widget_set_sensitive(toolbox, FALSE);
1334     }
1336 } // end of toolbox_set_desktop()
1339 static void
1340 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1342     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1343     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1344     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1345         // "toolbox.tools" was not set. Fallback to older value
1346         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1348         // Copy the setting forwards
1349         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1350     }
1351     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1353     for (int i = 0 ; tools[i].type_name ; i++ ) {
1354         GtkWidget *button =
1355             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1356                                                               SP_BUTTON_TYPE_TOGGLE,
1357                                                               Inkscape::Verb::get(tools[i].verb),
1358                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1359                                                               desktop,
1360                                                               tooltips );
1362         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1363                            (gpointer)button );
1364     }
1368 static void
1369 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1371     gchar const *const tname = ( eventcontext
1372                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1373                                  : NULL );
1374     for (int i = 0 ; tools[i].type_name ; i++ ) {
1375         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1376         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1377     }
1380 static void
1381 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1383     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1384     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1385     GtkUIManager* mgr = gtk_ui_manager_new();
1386     GError* errVal = 0;
1387     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1388     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1390     std::map<std::string, GtkWidget*> dataHolders;
1392     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1393         if ( aux_toolboxes[i].prep_func ) {
1394             // converted to GtkActions and UIManager
1396             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1397             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1398             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1399             dataHolders[aux_toolboxes[i].type_name] = kludge;
1400             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1401         } else {
1403             GtkWidget *sub_toolbox = 0;
1404             if (aux_toolboxes[i].create_func == NULL)
1405                 sub_toolbox = sp_empty_toolbox_new(desktop);
1406             else {
1407                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1408             }
1410             gtk_size_group_add_widget( grouper, sub_toolbox );
1412             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1413             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1415         }
1416     }
1418     // Second pass to create toolbars *after* all GtkActions are created
1419     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1420         if ( aux_toolboxes[i].prep_func ) {
1421             // converted to GtkActions and UIManager
1423             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1425             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1426             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1428             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1429             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1430             g_free( tmp );
1431             tmp = 0;
1433             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1434             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1435             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1436                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1437             }
1438             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1441             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1443             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1444                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1445                 swatch->setDesktop( desktop );
1446                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1447                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1448                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1449                 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 );
1450             }
1452             gtk_widget_show_all( holder );
1453             sp_set_font_size_smaller( holder );
1455             gtk_size_group_add_widget( grouper, holder );
1457             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1458             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1459         }
1460     }
1462     g_object_unref( G_OBJECT(grouper) );
1465 static void
1466 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1468     gchar const *tname = ( eventcontext
1469                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1470                            : NULL );
1471     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1472         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1473         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1474             gtk_widget_show_all(sub_toolbox);
1475             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1476         } else {
1477             gtk_widget_hide(sub_toolbox);
1478         }
1479     }
1482 static void
1483 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1485     gchar const * descr =
1486         "<ui>"
1487         "  <toolbar name='CommandsToolbar'>"
1488         "    <toolitem action='FileNew' />"
1489         "    <toolitem action='FileOpen' />"
1490         "    <toolitem action='FileSave' />"
1491         "    <toolitem action='FilePrint' />"
1492         "    <separator />"
1493         "    <toolitem action='FileImport' />"
1494         "    <toolitem action='FileExport' />"
1495         "    <separator />"
1496         "    <toolitem action='EditUndo' />"
1497         "    <toolitem action='EditRedo' />"
1498         "    <separator />"
1499         "    <toolitem action='EditCopy' />"
1500         "    <toolitem action='EditCut' />"
1501         "    <toolitem action='EditPaste' />"
1502         "    <separator />"
1503         "    <toolitem action='ZoomSelection' />"
1504         "    <toolitem action='ZoomDrawing' />"
1505         "    <toolitem action='ZoomPage' />"
1506         "    <separator />"
1507         "    <toolitem action='EditDuplicate' />"
1508         "    <toolitem action='EditClone' />"
1509         "    <toolitem action='EditUnlinkClone' />"
1510         "    <separator />"
1511         "    <toolitem action='SelectionGroup' />"
1512         "    <toolitem action='SelectionUnGroup' />"
1513         "    <separator />"
1514         "    <toolitem action='DialogFillStroke' />"
1515         "    <toolitem action='DialogText' />"
1516         "    <toolitem action='DialogXMLEditor' />"
1517         "    <toolitem action='DialogAlignDistribute' />"
1518         "    <separator />"
1519         "    <toolitem action='DialogPreferences' />"
1520         "    <toolitem action='DialogDocumentProperties' />"
1521         "  </toolbar>"
1522         "</ui>";
1523     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1526     GtkUIManager* mgr = gtk_ui_manager_new();
1527     GError* errVal = 0;
1529     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1530     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1532     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1533     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1534         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1535     }
1536     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1537     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1538     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1541     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1544 static void
1545 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1549 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1551     gtk_widget_show(toolbox_toplevel);
1552     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1554     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1555     if (!shown_toolbox) {
1556         return;
1557     }
1558     gtk_widget_show(toolbox);
1560     gtk_widget_show_all(shown_toolbox);
1563 void
1564 aux_toolbox_space(GtkWidget *tb, gint space)
1566     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1569 static GtkWidget *
1570 sp_empty_toolbox_new(SPDesktop *desktop)
1572     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1573     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1574     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1576     gtk_widget_show_all(tbl);
1577     sp_set_font_size_smaller (tbl);
1579     return tbl;
1582 // helper UI functions
1584 GtkWidget *
1585 sp_tb_spinbutton(
1586     gchar *label, gchar const *tooltip,
1587     gchar const *path, gchar const *data, gdouble def,
1588     GtkWidget *us,
1589     GtkWidget *tbl,
1590     gboolean altx, gchar const *altx_mark,
1591     gdouble lower, gdouble upper, gdouble step, gdouble page,
1592     void (*callback)(GtkAdjustment *, GtkWidget *),
1593     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1595     GtkTooltips *tt = gtk_tooltips_new();
1597     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1599     GtkWidget *l = gtk_label_new(label);
1600     gtk_widget_show(l);
1601     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1602     gtk_container_add(GTK_CONTAINER(hb), l);
1604     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1605                                       lower, upper, step, page, page);
1606     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1607     if (us)
1608         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1610     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1611     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1612     if (altx)
1613         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1614     gtk_widget_set_size_request(sb,
1615                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1616                                 AUX_SPINBUTTON_HEIGHT);
1617     gtk_widget_show(sb);
1618     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1619     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1620     gtk_container_add(GTK_CONTAINER(hb), sb);
1621     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1623     return hb;
1626 #define MODE_LABEL_WIDTH 70
1628 //########################
1629 //##       Star         ##
1630 //########################
1632 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1634     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1636     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1637         // do not remember prefs if this call is initiated by an undo change, because undoing object
1638         // creation sets bogus values to its attributes before it is deleted
1639         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1640     }
1642     // quit if run by the attr_changed listener
1643     if (g_object_get_data( dataKludge, "freeze" )) {
1644         return;
1645     }
1647     // in turn, prevent listener from responding
1648     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1650     bool modmade = false;
1652     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1653     GSList const *items = selection->itemList();
1654     for (; items != NULL; items = items->next) {
1655         if (SP_IS_STAR((SPItem *) items->data)) {
1656             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1657             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1658             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1659                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1660                                     + M_PI / (gint)adj->value));
1661             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1662             modmade = true;
1663         }
1664     }
1665     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1666                                    _("Star: Change number of corners"));
1668     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1671 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1673     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1675     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1676         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1677     }
1679     // quit if run by the attr_changed listener
1680     if (g_object_get_data( dataKludge, "freeze" )) {
1681         return;
1682     }
1684     // in turn, prevent listener from responding
1685     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1687     bool modmade = false;
1688     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1689     GSList const *items = selection->itemList();
1690     for (; items != NULL; items = items->next) {
1691         if (SP_IS_STAR((SPItem *) items->data)) {
1692             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1694             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1695             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1696             if (r2 < r1) {
1697                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1698             } else {
1699                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1700             }
1702             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1703             modmade = true;
1704         }
1705     }
1707     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1708                                    _("Star: Change spoke ratio"));
1710     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1713 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1715     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1716     bool flat = ege_select_one_action_get_active( act ) == 0;
1718     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1719         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1720                                     flat ? "true" : "false" );
1721     }
1723     // quit if run by the attr_changed listener
1724     if (g_object_get_data( dataKludge, "freeze" )) {
1725         return;
1726     }
1728     // in turn, prevent listener from responding
1729     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1731     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1732     GSList const *items = selection->itemList();
1733     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1734     bool modmade = false;
1736     if ( prop_action ) {
1737         gtk_action_set_sensitive( prop_action, !flat );
1738     }
1740     for (; items != NULL; items = items->next) {
1741         if (SP_IS_STAR((SPItem *) items->data)) {
1742             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1743             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1744             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1745             modmade = true;
1746         }
1747     }
1749     if (modmade) {
1750         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1751                          flat ? _("Make polygon") : _("Make star"));
1752     }
1754     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1757 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1759     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1761     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1762         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1763     }
1765     // quit if run by the attr_changed listener
1766     if (g_object_get_data( dataKludge, "freeze" )) {
1767         return;
1768     }
1770     // in turn, prevent listener from responding
1771     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1773     bool modmade = false;
1775     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1776     GSList const *items = selection->itemList();
1777     for (; items != NULL; items = items->next) {
1778         if (SP_IS_STAR((SPItem *) items->data)) {
1779             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1780             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1781             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1782             modmade = true;
1783         }
1784     }
1785     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1786                                    _("Star: Change rounding"));
1788     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1791 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1793     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1795     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1796         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1797     }
1799     // quit if run by the attr_changed listener
1800     if (g_object_get_data( dataKludge, "freeze" )) {
1801         return;
1802     }
1804     // in turn, prevent listener from responding
1805     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1807     bool modmade = false;
1809     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1810     GSList const *items = selection->itemList();
1811     for (; items != NULL; items = items->next) {
1812         if (SP_IS_STAR((SPItem *) items->data)) {
1813             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1814             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1815             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1816             modmade = true;
1817         }
1818     }
1819     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1820                                    _("Star: Change randomization"));
1822     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1826 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1827                                        gchar const */*old_value*/, gchar const */*new_value*/,
1828                                        bool /*is_interactive*/, gpointer data)
1830     GtkWidget *tbl = GTK_WIDGET(data);
1832     // quit if run by the _changed callbacks
1833     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1834         return;
1835     }
1837     // in turn, prevent callbacks from responding
1838     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1840     GtkAdjustment *adj = 0;
1842     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1843     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1845     if (!strcmp(name, "inkscape:randomized")) {
1846         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1847         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1848     } else if (!strcmp(name, "inkscape:rounded")) {
1849         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1850         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1851     } else if (!strcmp(name, "inkscape:flatsided")) {
1852         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1853         char const *flatsides = repr->attribute("inkscape:flatsided");
1854         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1855         if ( flatsides && !strcmp(flatsides,"false") ) {
1856             ege_select_one_action_set_active( flat_action, 1 );
1857             gtk_action_set_sensitive( prop_action, TRUE );
1858         } else {
1859             ege_select_one_action_set_active( flat_action, 0 );
1860             gtk_action_set_sensitive( prop_action, FALSE );
1861         }
1862     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1863         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1864         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1865         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1866         if (r2 < r1) {
1867             gtk_adjustment_set_value(adj, r2/r1);
1868         } else {
1869             gtk_adjustment_set_value(adj, r1/r2);
1870         }
1871     } else if (!strcmp(name, "sodipodi:sides")) {
1872         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1873         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1874     }
1876     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1880 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1882     NULL, /* child_added */
1883     NULL, /* child_removed */
1884     star_tb_event_attr_changed,
1885     NULL, /* content_changed */
1886     NULL  /* order_changed */
1887 };
1890 /**
1891  *  \param selection Should not be NULL.
1892  */
1893 static void
1894 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1896     int n_selected = 0;
1897     Inkscape::XML::Node *repr = NULL;
1899     purge_repr_listener( tbl, tbl );
1901     for (GSList const *items = selection->itemList();
1902          items != NULL;
1903          items = items->next)
1904     {
1905         if (SP_IS_STAR((SPItem *) items->data)) {
1906             n_selected++;
1907             repr = SP_OBJECT_REPR((SPItem *) items->data);
1908         }
1909     }
1911     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1913     if (n_selected == 0) {
1914         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1915     } else if (n_selected == 1) {
1916         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1918         if (repr) {
1919             g_object_set_data( tbl, "repr", repr );
1920             Inkscape::GC::anchor(repr);
1921             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1922             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1923         }
1924     } else {
1925         // FIXME: implement averaging of all parameters for multiple selected stars
1926         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1927         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1928     }
1932 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1934     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1935     // callbacks to lump all the changes for all selected objects in one undo step
1937     GtkAdjustment *adj = 0;
1939     // fixme: make settable in prefs!
1940     gint mag = 5;
1941     gdouble prop = 0.5;
1942     gboolean flat = FALSE;
1943     gdouble randomized = 0;
1944     gdouble rounded = 0;
1946     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1947     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1949     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1950     gtk_action_set_sensitive( sb2, !flat );
1952     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1953     gtk_adjustment_set_value(adj, mag);
1954     gtk_adjustment_value_changed(adj);
1956     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1957     gtk_adjustment_set_value(adj, prop);
1958     gtk_adjustment_value_changed(adj);
1960     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1961     gtk_adjustment_set_value(adj, rounded);
1962     gtk_adjustment_value_changed(adj);
1964     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1965     gtk_adjustment_set_value(adj, randomized);
1966     gtk_adjustment_value_changed(adj);
1970 void
1971 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1973     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1974     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1975     GtkWidget *l = gtk_label_new(NULL);
1976     gtk_label_set_markup(GTK_LABEL(l), title);
1977     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1978     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1979     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1983 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1985     {
1986         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1987         ege_output_action_set_use_markup( act, TRUE );
1988         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1989         g_object_set_data( holder, "mode_action", act );
1990     }
1992     {
1993         EgeAdjustmentAction* eact = 0;
1994         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1995         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1997         /* Flatsided checkbox */
1998         {
1999             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2001             GtkTreeIter iter;
2002             gtk_list_store_append( model, &iter );
2003             gtk_list_store_set( model, &iter,
2004                                 0, _("Polygon"),
2005                                 1, _("Regular polygon (with one handle) instead of a star"),
2006                                 2, "star_flat",
2007                                 -1 );
2009             gtk_list_store_append( model, &iter );
2010             gtk_list_store_set( model, &iter,
2011                                 0, _("Star"),
2012                                 1, _("Star instead of a regular polygon (with one handle)"),
2013                                 2, "star_angled",
2014                                 -1 );
2016             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2017             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2018             g_object_set_data( holder, "flat_action", act );
2020             ege_select_one_action_set_appearance( act, "full" );
2021             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2022             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2023             ege_select_one_action_set_icon_column( act, 2 );
2024             ege_select_one_action_set_tooltip_column( act, 1  );
2026             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2027             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2028         }
2030         /* Magnitude */
2031         {
2032         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2033         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2034         eact = create_adjustment_action( "MagnitudeAction",
2035                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2036                                          "tools.shapes.star", "magnitude", 3,
2037                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2038                                          3, 1024, 1, 5,
2039                                          labels, values, G_N_ELEMENTS(labels),
2040                                          sp_stb_magnitude_value_changed,
2041                                          1.0, 0 );
2042         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2043         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2044         }
2046         /* Spoke ratio */
2047         {
2048         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2049         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2050         eact = create_adjustment_action( "SpokeAction",
2051                                          _("Spoke ratio"), _("Spoke ratio:"),
2052                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2053                                          // Base radius is the same for the closest handle.
2054                                          _("Base radius to tip radius ratio"),
2055                                          "tools.shapes.star", "proportion", 0.5,
2056                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2057                                          0.01, 1.0, 0.01, 0.1,
2058                                          labels, values, G_N_ELEMENTS(labels),
2059                                          sp_stb_proportion_value_changed );
2060         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2061         g_object_set_data( holder, "prop_action", eact );
2062         }
2064         if ( !isFlatSided ) {
2065             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2066         } else {
2067             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2068         }
2070         /* Roundedness */
2071         {
2072         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2073         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2074         eact = create_adjustment_action( "RoundednessAction",
2075                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2076                                          "tools.shapes.star", "rounded", 0.0,
2077                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2078                                          -10.0, 10.0, 0.01, 0.1,
2079                                          labels, values, G_N_ELEMENTS(labels),
2080                                          sp_stb_rounded_value_changed );
2081         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2082         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2083         }
2085         /* Randomization */
2086         {
2087         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2088         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2089         eact = create_adjustment_action( "RandomizationAction",
2090                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2091                                          "tools.shapes.star", "randomized", 0.0,
2092                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2093                                          -10.0, 10.0, 0.001, 0.01,
2094                                          labels, values, G_N_ELEMENTS(labels),
2095                                          sp_stb_randomized_value_changed, 0.1, 3 );
2096         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2097         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2098         }
2099     }
2101     {
2102         /* Reset */
2103         {
2104             GtkAction* act = gtk_action_new( "StarResetAction",
2105                                              _("Defaults"),
2106                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2107                                              GTK_STOCK_CLEAR );
2108             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2109             gtk_action_group_add_action( mainActions, act );
2110             gtk_action_set_sensitive( act, TRUE );
2111         }
2112     }
2114     sigc::connection *connection = new sigc::connection(
2115         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2116         );
2117     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2118     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2122 //########################
2123 //##       Rect         ##
2124 //########################
2126 static void sp_rtb_sensitivize( GObject *tbl )
2128     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2129     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2130     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2132     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2133         gtk_action_set_sensitive( not_rounded, FALSE );
2134     } else {
2135         gtk_action_set_sensitive( not_rounded, TRUE );
2136     }
2140 static void
2141 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2142                           void (*setter)(SPRect *, gdouble))
2144     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2146     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2147     SPUnit const *unit = tracker->getActiveUnit();
2149     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2150         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2151     }
2153     // quit if run by the attr_changed listener
2154     if (g_object_get_data( tbl, "freeze" )) {
2155         return;
2156     }
2158     // in turn, prevent listener from responding
2159     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2161     bool modmade = false;
2162     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2163     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2164         if (SP_IS_RECT(items->data)) {
2165             if (adj->value != 0) {
2166                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2167             } else {
2168                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2169             }
2170             modmade = true;
2171         }
2172     }
2174     sp_rtb_sensitivize( tbl );
2176     if (modmade) {
2177         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2178                                    _("Change rectangle"));
2179     }
2181     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2184 static void
2185 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2187     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2190 static void
2191 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2193     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2196 static void
2197 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2199     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2202 static void
2203 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2205     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2210 static void
2211 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2213     GtkAdjustment *adj = 0;
2215     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2216     gtk_adjustment_set_value(adj, 0.0);
2217     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2218     gtk_adjustment_value_changed(adj);
2220     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2221     gtk_adjustment_set_value(adj, 0.0);
2222     gtk_adjustment_value_changed(adj);
2224     sp_rtb_sensitivize( obj );
2227 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2228                                        gchar const */*old_value*/, gchar const */*new_value*/,
2229                                        bool /*is_interactive*/, gpointer data)
2231     GObject *tbl = G_OBJECT(data);
2233     // quit if run by the _changed callbacks
2234     if (g_object_get_data( tbl, "freeze" )) {
2235         return;
2236     }
2238     // in turn, prevent callbacks from responding
2239     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2241     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2242     SPUnit const *unit = tracker->getActiveUnit();
2244     gpointer item = g_object_get_data( tbl, "item" );
2245     if (item && SP_IS_RECT(item)) {
2246         {
2247             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2248             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2249             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2250         }
2252         {
2253             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2254             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2255             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2256         }
2258         {
2259             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2260             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2261             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2262         }
2264         {
2265             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2266             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2267             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2268         }
2269     }
2271     sp_rtb_sensitivize( tbl );
2273     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2277 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2278     NULL, /* child_added */
2279     NULL, /* child_removed */
2280     rect_tb_event_attr_changed,
2281     NULL, /* content_changed */
2282     NULL  /* order_changed */
2283 };
2285 /**
2286  *  \param selection should not be NULL.
2287  */
2288 static void
2289 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2291     int n_selected = 0;
2292     Inkscape::XML::Node *repr = NULL;
2293     SPItem *item = NULL;
2295     if ( g_object_get_data( tbl, "repr" ) ) {
2296         g_object_set_data( tbl, "item", NULL );
2297     }
2298     purge_repr_listener( tbl, tbl );
2300     for (GSList const *items = selection->itemList();
2301          items != NULL;
2302          items = items->next) {
2303         if (SP_IS_RECT((SPItem *) items->data)) {
2304             n_selected++;
2305             item = (SPItem *) items->data;
2306             repr = SP_OBJECT_REPR(item);
2307         }
2308     }
2310     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2312     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2314     if (n_selected == 0) {
2315         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2317         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2318         gtk_action_set_sensitive(w, FALSE);
2319         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2320         gtk_action_set_sensitive(h, FALSE);
2322     } else if (n_selected == 1) {
2323         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2324         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2326         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2327         gtk_action_set_sensitive(w, TRUE);
2328         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2329         gtk_action_set_sensitive(h, TRUE);
2331         if (repr) {
2332             g_object_set_data( tbl, "repr", repr );
2333             g_object_set_data( tbl, "item", item );
2334             Inkscape::GC::anchor(repr);
2335             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2336             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2337         }
2338     } else {
2339         // FIXME: implement averaging of all parameters for multiple selected
2340         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2341         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2342         sp_rtb_sensitivize( tbl );
2343     }
2347 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2349     EgeAdjustmentAction* eact = 0;
2351     {
2352         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2353         ege_output_action_set_use_markup( act, TRUE );
2354         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2355         g_object_set_data( holder, "mode_action", act );
2356     }
2358     // rx/ry units menu: create
2359     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2360     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2361     // fixme: add % meaning per cent of the width/height
2362     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2363     g_object_set_data( holder, "tracker", tracker );
2365     /* W */
2366     {
2367         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2368         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2369         eact = create_adjustment_action( "RectWidthAction",
2370                                          _("Width"), _("W:"), _("Width of rectangle"),
2371                                          "tools.shapes.rect", "width", 0,
2372                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2373                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2374                                          labels, values, G_N_ELEMENTS(labels),
2375                                          sp_rtb_width_value_changed );
2376         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2377         g_object_set_data( holder, "width_action", eact );
2378         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2379         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2380     }
2382     /* H */
2383     {
2384         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2385         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2386         eact = create_adjustment_action( "RectHeightAction",
2387                                          _("Height"), _("H:"), _("Height of rectangle"),
2388                                          "tools.shapes.rect", "height", 0,
2389                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2390                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2391                                          labels, values, G_N_ELEMENTS(labels),
2392                                          sp_rtb_height_value_changed );
2393         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2394         g_object_set_data( holder, "height_action", eact );
2395         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2396         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2397     }
2399     /* rx */
2400     {
2401         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2402         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2403         eact = create_adjustment_action( "RadiusXAction",
2404                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2405                                          "tools.shapes.rect", "rx", 0,
2406                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2407                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2408                                          labels, values, G_N_ELEMENTS(labels),
2409                                          sp_rtb_rx_value_changed);
2410         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2411         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2412     }
2414     /* ry */
2415     {
2416         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2417         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2418         eact = create_adjustment_action( "RadiusYAction",
2419                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2420                                          "tools.shapes.rect", "ry", 0,
2421                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2422                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2423                                          labels, values, G_N_ELEMENTS(labels),
2424                                          sp_rtb_ry_value_changed);
2425         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2426         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2427     }
2429     // add the units menu
2430     {
2431         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2432         gtk_action_group_add_action( mainActions, act );
2433     }
2435     /* Reset */
2436     {
2437         InkAction* inky = ink_action_new( "RectResetAction",
2438                                           _("Not rounded"),
2439                                           _("Make corners sharp"),
2440                                           "squared_corner",
2441                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2442         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2443         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2444         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2445         g_object_set_data( holder, "not_rounded", inky );
2446     }
2448     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2449     sp_rtb_sensitivize( holder );
2451     sigc::connection *connection = new sigc::connection(
2452         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2453         );
2454     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2455     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2458 //########################
2459 //##       3D Box       ##
2460 //########################
2462 // normalize angle so that it lies in the interval [0,360]
2463 static double box3d_normalize_angle (double a) {
2464     double angle = a + ((int) (a/360.0))*360;
2465     if (angle < 0) {
2466         angle += 360.0;
2467     }
2468     return angle;
2471 static void
2472 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2473                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2474     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2475     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2476     //       are reset).
2477     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2479     if (is_infinite) {
2480         gtk_toggle_action_set_active(tact, TRUE);
2481         gtk_action_set_sensitive(act, TRUE);
2483         double angle = persp3d_get_infinite_angle(persp, axis);
2484         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2485             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2486         }
2487     } else {
2488         gtk_toggle_action_set_active(tact, FALSE);
2489         gtk_action_set_sensitive(act, FALSE);
2490     }
2493 static void
2494 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2495     if (!persp_repr) {
2496         g_print ("No perspective given to box3d_resync_toolbar().\n");
2497         return;
2498     }
2500     GtkWidget *tbl = GTK_WIDGET(data);
2501     GtkAdjustment *adj = 0;
2502     GtkAction *act = 0;
2503     GtkToggleAction *tact = 0;
2504     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2505     {
2506         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2507         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2508         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2510         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2511     }
2512     {
2513         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2514         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2515         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2517         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2518     }
2519     {
2520         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2521         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2522         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2524         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2525     }
2528 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2529                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2530                                                   bool /*is_interactive*/, gpointer data)
2532     GtkWidget *tbl = GTK_WIDGET(data);
2534     // quit if run by the attr_changed listener
2535     // note: it used to work without the differently called freeze_ attributes (here and in
2536     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2537     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2538         return;
2539     }
2541     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2542     // sp_document_maybe_done() when the document is undo insensitive)
2543     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2545     // TODO: Only update the appropriate part of the toolbar
2546 //    if (!strcmp(name, "inkscape:vp_z")) {
2547         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2548 //    }
2550     Persp3D *persp = persp3d_get_from_repr(repr);
2551     persp3d_update_box_reprs(persp);
2553     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2556 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2558     NULL, /* child_added */
2559     NULL, /* child_removed */
2560     box3d_persp_tb_event_attr_changed,
2561     NULL, /* content_changed */
2562     NULL  /* order_changed */
2563 };
2565 /**
2566  *  \param selection Should not be NULL.
2567  */
2568 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2569 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2570 static void
2571 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2573     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2574     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2575     // update the perspectives with infinite VPs and leave the other ones untouched).
2577     Inkscape::XML::Node *persp_repr = NULL;
2578     purge_repr_listener(tbl, tbl);
2580     SPItem *item = selection->singleItem();
2581     if (item && SP_IS_BOX3D(item)) {
2582         // FIXME: Also deal with multiple selected boxes
2583         SPBox3D *box = SP_BOX3D(item);
2584         Persp3D *persp = box3d_get_perspective(box);
2585         persp_repr = SP_OBJECT_REPR(persp);
2586         if (persp_repr) {
2587             g_object_set_data(tbl, "repr", persp_repr);
2588             Inkscape::GC::anchor(persp_repr);
2589             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2590             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2591         }
2593         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2594         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2596         box3d_resync_toolbar(persp_repr, tbl);
2597     }
2600 static void
2601 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2603     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2604     SPDocument *document = sp_desktop_document(desktop);
2606     // quit if run by the attr_changed listener
2607     // note: it used to work without the differently called freeze_ attributes (here and in
2608     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2609     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2610         return;
2611     }
2613     // in turn, prevent listener from responding
2614     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2616     //Persp3D *persp = document->current_persp3d;
2617     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2618     if (sel_persps.empty()) {
2619         // this can happen when the document is created; we silently ignore it
2620         return;
2621     }
2622     Persp3D *persp = *(sel_persps.begin());
2624     persp->tmat.set_infinite_direction (axis, adj->value);
2625     SP_OBJECT(persp)->updateRepr();
2627     // TODO: use the correct axis here, too
2628     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2630     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2634 static void
2635 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2637     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2640 static void
2641 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2643     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2646 static void
2647 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2649     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2653 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2655     // TODO: Take all selected perspectives into account
2656     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2657     if (sel_persps.empty()) {
2658         // this can happen when the document is created; we silently ignore it
2659         return;
2660     }
2661     Persp3D *persp = *(sel_persps.begin());
2663     bool set_infinite = gtk_toggle_action_get_active(act);
2664     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2667 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2669     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2672 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2674     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2677 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2679     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2682 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2684     EgeAdjustmentAction* eact = 0;
2685     SPDocument *document = sp_desktop_document (desktop);
2686     Persp3D *persp = document->current_persp3d;
2688     EgeAdjustmentAction* box3d_angle_x = 0;
2689     EgeAdjustmentAction* box3d_angle_y = 0;
2690     EgeAdjustmentAction* box3d_angle_z = 0;
2692     /* Angle X */
2693     {
2694         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2695         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2696         eact = create_adjustment_action( "3DBoxAngleXAction",
2697                                          _("Angle in X direction"), _("Angle X:"),
2698                                          // Translators: PL is short for 'perspective line'
2699                                          _("Angle of PLs in X direction"),
2700                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2701                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2702                                          -360.0, 360.0, 1.0, 10.0,
2703                                          labels, values, G_N_ELEMENTS(labels),
2704                                          box3d_angle_x_value_changed );
2705         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2706         g_object_set_data( holder, "box3d_angle_x_action", eact );
2707         box3d_angle_x = eact;
2708     }
2710     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2711         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2712     } else {
2713         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2714     }
2717     /* VP X state */
2718     {
2719         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2720                                                       // Translators: VP is short for 'vanishing point'
2721                                                       _("State of VP in X direction"),
2722                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2723                                                       "toggle_vp_x",
2724                                                       Inkscape::ICON_SIZE_DECORATION );
2725         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2726         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2727         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2728         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2729         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2730     }
2732     /* Angle Y */
2733     {
2734         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2735         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2736         eact = create_adjustment_action( "3DBoxAngleYAction",
2737                                          _("Angle in Y direction"), _("Angle Y:"),
2738                                          // Translators: PL is short for 'perspective line'
2739                                          _("Angle of PLs in Y direction"),
2740                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2741                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2742                                          -360.0, 360.0, 1.0, 10.0,
2743                                          labels, values, G_N_ELEMENTS(labels),
2744                                          box3d_angle_y_value_changed );
2745         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2746         g_object_set_data( holder, "box3d_angle_y_action", eact );
2747         box3d_angle_y = eact;
2748     }
2750     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2751         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2752     } else {
2753         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2754     }
2756     /* VP Y state */
2757     {
2758         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2759                                                       // Translators: VP is short for 'vanishing point'
2760                                                       _("State of VP in Y direction"),
2761                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2762                                                       "toggle_vp_y",
2763                                                       Inkscape::ICON_SIZE_DECORATION );
2764         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2765         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2766         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2767         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2768         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2769     }
2771     /* Angle Z */
2772     {
2773         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2774         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2775         eact = create_adjustment_action( "3DBoxAngleZAction",
2776                                          _("Angle in Z direction"), _("Angle Z:"),
2777                                          // Translators: PL is short for 'perspective line'
2778                                          _("Angle of PLs in Z direction"),
2779                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2780                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2781                                          -360.0, 360.0, 1.0, 10.0,
2782                                          labels, values, G_N_ELEMENTS(labels),
2783                                          box3d_angle_z_value_changed );
2784         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2785         g_object_set_data( holder, "box3d_angle_z_action", eact );
2786         box3d_angle_z = eact;
2787     }
2789     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2790         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2791     } else {
2792         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2793     }
2795     /* VP Z state */
2796     {
2797         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2798                                                       // Translators: VP is short for 'vanishing point'
2799                                                       _("State of VP in Z direction"),
2800                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2801                                                       "toggle_vp_z",
2802                                                       Inkscape::ICON_SIZE_DECORATION );
2803         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2804         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2805         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2806         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2807         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2808     }
2810     sigc::connection *connection = new sigc::connection(
2811         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2812        );
2813     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2814     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2817 //########################
2818 //##       Spiral       ##
2819 //########################
2821 static void
2822 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2824     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2826     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2827         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2828     }
2830     // quit if run by the attr_changed listener
2831     if (g_object_get_data( tbl, "freeze" )) {
2832         return;
2833     }
2835     // in turn, prevent listener from responding
2836     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2838     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2840     bool modmade = false;
2841     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2842          items != NULL;
2843          items = items->next)
2844     {
2845         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2846             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2847             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2848             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2849             modmade = true;
2850         }
2851     }
2853     g_free(namespaced_name);
2855     if (modmade) {
2856         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2857                                    _("Change spiral"));
2858     }
2860     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2863 static void
2864 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2866     sp_spl_tb_value_changed(adj, tbl, "revolution");
2869 static void
2870 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2872     sp_spl_tb_value_changed(adj, tbl, "expansion");
2875 static void
2876 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2878     sp_spl_tb_value_changed(adj, tbl, "t0");
2881 static void
2882 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2884     GtkWidget *tbl = GTK_WIDGET(obj);
2886     GtkAdjustment *adj;
2888     // fixme: make settable
2889     gdouble rev = 5;
2890     gdouble exp = 1.0;
2891     gdouble t0 = 0.0;
2893     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2894     gtk_adjustment_set_value(adj, rev);
2895     gtk_adjustment_value_changed(adj);
2897     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2898     gtk_adjustment_set_value(adj, exp);
2899     gtk_adjustment_value_changed(adj);
2901     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2902     gtk_adjustment_set_value(adj, t0);
2903     gtk_adjustment_value_changed(adj);
2905     spinbutton_defocus(GTK_OBJECT(tbl));
2909 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2910                                          gchar const */*old_value*/, gchar const */*new_value*/,
2911                                          bool /*is_interactive*/, gpointer data)
2913     GtkWidget *tbl = GTK_WIDGET(data);
2915     // quit if run by the _changed callbacks
2916     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2917         return;
2918     }
2920     // in turn, prevent callbacks from responding
2921     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2923     GtkAdjustment *adj;
2924     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2925     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2927     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2928     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2930     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2931     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2933     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2937 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2938     NULL, /* child_added */
2939     NULL, /* child_removed */
2940     spiral_tb_event_attr_changed,
2941     NULL, /* content_changed */
2942     NULL  /* order_changed */
2943 };
2945 static void
2946 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2948     int n_selected = 0;
2949     Inkscape::XML::Node *repr = NULL;
2951     purge_repr_listener( tbl, tbl );
2953     for (GSList const *items = selection->itemList();
2954          items != NULL;
2955          items = items->next)
2956     {
2957         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2958             n_selected++;
2959             repr = SP_OBJECT_REPR((SPItem *) items->data);
2960         }
2961     }
2963     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2965     if (n_selected == 0) {
2966         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2967     } else if (n_selected == 1) {
2968         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2970         if (repr) {
2971             g_object_set_data( tbl, "repr", repr );
2972             Inkscape::GC::anchor(repr);
2973             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2974             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2975         }
2976     } else {
2977         // FIXME: implement averaging of all parameters for multiple selected
2978         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2979         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2980     }
2984 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2986     EgeAdjustmentAction* eact = 0;
2988     {
2989         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2990         ege_output_action_set_use_markup( act, TRUE );
2991         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2992         g_object_set_data( holder, "mode_action", act );
2993     }
2995     /* Revolution */
2996     {
2997         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2998         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2999         eact = create_adjustment_action( "SpiralRevolutionAction",
3000                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3001                                          "tools.shapes.spiral", "revolution", 3.0,
3002                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3003                                          0.01, 1024.0, 0.1, 1.0,
3004                                          labels, values, G_N_ELEMENTS(labels),
3005                                          sp_spl_tb_revolution_value_changed, 1, 2);
3006         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3007     }
3009     /* Expansion */
3010     {
3011         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3012         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3013         eact = create_adjustment_action( "SpiralExpansionAction",
3014                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3015                                          "tools.shapes.spiral", "expansion", 1.0,
3016                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3017                                          0.0, 1000.0, 0.01, 1.0,
3018                                          labels, values, G_N_ELEMENTS(labels),
3019                                          sp_spl_tb_expansion_value_changed);
3020         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3021     }
3023     /* T0 */
3024     {
3025         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3026         gdouble values[] = {0, 0.5, 0.9};
3027         eact = create_adjustment_action( "SpiralT0Action",
3028                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3029                                          "tools.shapes.spiral", "t0", 0.0,
3030                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3031                                          0.0, 0.999, 0.01, 1.0,
3032                                          labels, values, G_N_ELEMENTS(labels),
3033                                          sp_spl_tb_t0_value_changed);
3034         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3035     }
3037     /* Reset */
3038     {
3039         InkAction* inky = ink_action_new( "SpiralResetAction",
3040                                           _("Defaults"),
3041                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3042                                           GTK_STOCK_CLEAR,
3043                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3044         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3045         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3046     }
3049     sigc::connection *connection = new sigc::connection(
3050         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3051         );
3052     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3053     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3056 //########################
3057 //##     Pen/Pencil    ##
3058 //########################
3061 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3063     // Put stuff here
3066 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3068     // Put stuff here
3071 //########################
3072 //##       Tweak        ##
3073 //########################
3075 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3077     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3080 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3082     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3085 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3087     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3090 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3092     int mode = ege_select_one_action_get_active( act );
3093     prefs_set_int_attribute("tools.tweak", "mode", mode);
3095     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3096     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3097     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3098     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3099     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3100     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3101     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3102         if (doh) gtk_action_set_sensitive (doh, TRUE);
3103         if (dos) gtk_action_set_sensitive (dos, TRUE);
3104         if (dol) gtk_action_set_sensitive (dol, TRUE);
3105         if (doo) gtk_action_set_sensitive (doo, TRUE);
3106         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3107         if (fid) gtk_action_set_sensitive (fid, FALSE);
3108     } else {
3109         if (doh) gtk_action_set_sensitive (doh, FALSE);
3110         if (dos) gtk_action_set_sensitive (dos, FALSE);
3111         if (dol) gtk_action_set_sensitive (dol, FALSE);
3112         if (doo) gtk_action_set_sensitive (doo, FALSE);
3113         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3114         if (fid) gtk_action_set_sensitive (fid, TRUE);
3115     }
3118 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3120     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3123 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3124     bool show = gtk_toggle_action_get_active( act );
3125     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3127 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3128     bool show = gtk_toggle_action_get_active( act );
3129     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3131 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3132     bool show = gtk_toggle_action_get_active( act );
3133     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3135 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3136     bool show = gtk_toggle_action_get_active( act );
3137     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3140 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3142     {
3143         /* Width */
3144         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3145         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3146         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3147                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3148                                                               "tools.tweak", "width", 15,
3149                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3150                                                               1, 100, 1.0, 10.0,
3151                                                               labels, values, G_N_ELEMENTS(labels),
3152                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3153         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3154         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3155     }
3158     {
3159         /* Force */
3160         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3161         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3162         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3163                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3164                                                               "tools.tweak", "force", 20,
3165                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3166                                                               1, 100, 1.0, 10.0,
3167                                                               labels, values, G_N_ELEMENTS(labels),
3168                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3169         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3170         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3171     }
3173     /* Mode */
3174     {
3175         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3177         GtkTreeIter iter;
3178         gtk_list_store_append( model, &iter );
3179         gtk_list_store_set( model, &iter,
3180                             0, _("Push mode"),
3181                             1, _("Push parts of paths in any direction"),
3182                             2, "tweak_push_mode",
3183                             -1 );
3185         gtk_list_store_append( model, &iter );
3186         gtk_list_store_set( model, &iter,
3187                             0, _("Shrink mode"),
3188                             1, _("Shrink (inset) parts of paths"),
3189                             2, "tweak_shrink_mode",
3190                             -1 );
3192         gtk_list_store_append( model, &iter );
3193         gtk_list_store_set( model, &iter,
3194                             0, _("Grow mode"),
3195                             1, _("Grow (outset) parts of paths"),
3196                             2, "tweak_grow_mode",
3197                             -1 );
3199         gtk_list_store_append( model, &iter );
3200         gtk_list_store_set( model, &iter,
3201                             0, _("Attract mode"),
3202                             1, _("Attract parts of paths towards cursor"),
3203                             2, "tweak_attract_mode",
3204                             -1 );
3206         gtk_list_store_append( model, &iter );
3207         gtk_list_store_set( model, &iter,
3208                             0, _("Repel mode"),
3209                             1, _("Repel parts of paths from cursor"),
3210                             2, "tweak_repel_mode",
3211                             -1 );
3213         gtk_list_store_append( model, &iter );
3214         gtk_list_store_set( model, &iter,
3215                             0, _("Roughen mode"),
3216                             1, _("Roughen parts of paths"),
3217                             2, "tweak_roughen_mode",
3218                             -1 );
3220         gtk_list_store_append( model, &iter );
3221         gtk_list_store_set( model, &iter,
3222                             0, _("Color paint mode"),
3223                             1, _("Paint the tool's color upon selected objects"),
3224                             2, "tweak_colorpaint_mode",
3225                             -1 );
3227         gtk_list_store_append( model, &iter );
3228         gtk_list_store_set( model, &iter,
3229                             0, _("Color jitter mode"),
3230                             1, _("Jitter the colors of selected objects"),
3231                             2, "tweak_colorjitter_mode",
3232                             -1 );
3234         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3235         g_object_set( act, "short_label", _("Mode:"), NULL );
3236         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3237         g_object_set_data( holder, "mode_action", act );
3239         ege_select_one_action_set_appearance( act, "full" );
3240         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3241         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3242         ege_select_one_action_set_icon_column( act, 2 );
3243         ege_select_one_action_set_tooltip_column( act, 1  );
3245         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3246         ege_select_one_action_set_active( act, mode );
3247         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3249         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3250     }
3252     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3254     {
3255         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3256         ege_output_action_set_use_markup( act, TRUE );
3257         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3258         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3259             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3260         g_object_set_data( holder, "tweak_channels_label", act);
3261     }
3263     {
3264         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3265                                                       _("Hue"),
3266                                                       _("In color mode, act on objects' hue"),
3267                                                       NULL,
3268                                                       Inkscape::ICON_SIZE_DECORATION );
3269         g_object_set( act, "short_label", _("H"), NULL );
3270         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3271         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3272         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3273         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3274             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3275         g_object_set_data( holder, "tweak_doh", act);
3276     }
3277     {
3278         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3279                                                       _("Saturation"),
3280                                                       _("In color mode, act on objects' saturation"),
3281                                                       NULL,
3282                                                       Inkscape::ICON_SIZE_DECORATION );
3283         g_object_set( act, "short_label", _("S"), NULL );
3284         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3285         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3286         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3287         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3288             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3289         g_object_set_data( holder, "tweak_dos", act );
3290     }
3291     {
3292         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3293                                                       _("Lightness"),
3294                                                       _("In color mode, act on objects' lightness"),
3295                                                       NULL,
3296                                                       Inkscape::ICON_SIZE_DECORATION );
3297         g_object_set( act, "short_label", _("L"), NULL );
3298         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3299         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3300         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3301         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3302             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3303         g_object_set_data( holder, "tweak_dol", act );
3304     }
3305     {
3306         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3307                                                       _("Opacity"),
3308                                                       _("In color mode, act on objects' opacity"),
3309                                                       NULL,
3310                                                       Inkscape::ICON_SIZE_DECORATION );
3311         g_object_set( act, "short_label", _("O"), NULL );
3312         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3313         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3314         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3315         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3316             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3317         g_object_set_data( holder, "tweak_doo", act );
3318     }
3320     {   /* Fidelity */
3321         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3322         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3323         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3324                                                               _("Fidelity"), _("Fidelity:"),
3325                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3326                                                               "tools.tweak", "fidelity", 50,
3327                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3328                                                               1, 100, 1.0, 10.0,
3329                                                               labels, values, G_N_ELEMENTS(labels),
3330                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3331         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3332         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3333         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3334             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3335         g_object_set_data( holder, "tweak_fidelity", eact );
3336     }
3339     /* Use Pressure button */
3340     {
3341         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3342                                                       _("Pressure"),
3343                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3344                                                       "use_pressure",
3345                                                       Inkscape::ICON_SIZE_DECORATION );
3346         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3347         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3348         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3349     }
3354 //########################
3355 //##     Calligraphy    ##
3356 //########################
3358 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3360     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3363 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3365     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3368 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3370     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3373 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3375     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3378 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3380     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3383 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3385     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3388 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3390     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3393 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3395     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3398 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3400     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3403 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3405     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3408 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3410     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3412     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3415 struct KeyValue {
3416     char const *key;
3417     double value;
3418 };
3422 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3423     struct ProfileElement {
3424         char const *name;
3425         double def;
3426         double min;
3427         double max;
3428     };
3429     ProfileElement profile[] = {
3430         {"mass",0.02, 0.0, 1.0},
3431         {"wiggle",0.0, 0.0, 1.0},
3432         {"angle",30.0, -90.0, 90.0},
3433         {"width",15.0, 1.0, 100.0},
3434         {"thinning",0.1, -1.0, 1.0},
3435         {"tremor",0.0, 0.0, 1.0},
3436         {"flatness",0.9, 0.0, 1.0},
3437         {"cap_rounding",0.0, 0.0, 5.0}
3438     };
3439     
3440     gint preset_index = ege_select_one_action_get_active( act );   
3441     gchar *pref_path = g_strdup_printf("tools.calligraphic.preset.cp%d", preset_index); 
3442     for (unsigned i = 0; i < 8; ++i) {
3443         ProfileElement const &pe = profile[i];
3444         double value = prefs_get_double_attribute_limited(pref_path,pe.name, pe.def, pe.min, pe.max);
3445         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3446         if ( adj ) {
3447             gtk_adjustment_set_value(adj, value);
3448         }
3449     } 
3450     free(pref_path);
3455 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3457     {
3458         EgeAdjustmentAction* calligraphy_angle = 0;
3460         {
3461         /* Width */
3462         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3463         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3464         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3465                                                               _("Pen Width"), _("Width:"),
3466                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3467                                                               "tools.calligraphic", "width", 15,
3468                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3469                                                               1, 100, 1.0, 10.0,
3470                                                               labels, values, G_N_ELEMENTS(labels),
3471                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3472         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3473         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3474         }
3476         {
3477         /* Thinning */
3478             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3479             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3480         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3481                                                               _("Stroke Thinning"), _("Thinning:"),
3482                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3483                                                               "tools.calligraphic", "thinning", 0.1,
3484                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3485                                                               -1.0, 1.0, 0.01, 0.1,
3486                                                               labels, values, G_N_ELEMENTS(labels),
3487                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3488         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3489         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3490         }
3492         {
3493         /* Angle */
3494         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3495         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3496         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3497                                                               _("Pen Angle"), _("Angle:"),
3498                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3499                                                               "tools.calligraphic", "angle", 30,
3500                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3501                                                               -90.0, 90.0, 1.0, 10.0,
3502                                                               labels, values, G_N_ELEMENTS(labels),
3503                                                               sp_ddc_angle_value_changed, 1, 0 );
3504         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3505         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3506         calligraphy_angle = eact;
3507         }
3509         {
3510         /* Fixation */
3511             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3512         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3513         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3514                                                               _("Fixation"), _("Fixation:"),
3515                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3516                                                               "tools.calligraphic", "flatness", 0.9,
3517                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3518                                                               0.0, 1.0, 0.01, 0.1,
3519                                                               labels, values, G_N_ELEMENTS(labels),
3520                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3521         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3522         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3523         }
3525         {
3526         /* Cap Rounding */
3527             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3528         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3529         // TRANSLATORS: "cap" means "end" (both start and finish) here
3530         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3531                                                               _("Cap rounding"), _("Caps:"),
3532                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3533                                                               "tools.calligraphic", "cap_rounding", 0.0,
3534                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3535                                                               0.0, 5.0, 0.01, 0.1,
3536                                                               labels, values, G_N_ELEMENTS(labels),
3537                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3538         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3539         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3540         }
3542         {
3543         /* Tremor */
3544             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3545         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3546         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3547                                                               _("Stroke Tremor"), _("Tremor:"),
3548                                                               _("Increase to make strokes rugged and trembling"),
3549                                                               "tools.calligraphic", "tremor", 0.0,
3550                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3551                                                               0.0, 1.0, 0.01, 0.1,
3552                                                               labels, values, G_N_ELEMENTS(labels),
3553                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3555         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3556         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3557         }
3559         {
3560         /* Wiggle */
3561         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3562         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3563         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3564                                                               _("Pen Wiggle"), _("Wiggle:"),
3565                                                               _("Increase to make the pen waver and wiggle"),
3566                                                               "tools.calligraphic", "wiggle", 0.0,
3567                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3568                                                               0.0, 1.0, 0.01, 0.1,
3569                                                               labels, values, G_N_ELEMENTS(labels),
3570                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3571         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3572         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3573         }
3575         {
3576         /* Mass */
3577             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3578         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3579         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3580                                                               _("Pen Mass"), _("Mass:"),
3581                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3582                                                               "tools.calligraphic", "mass", 0.02,
3583                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3584                                                               0.0, 1.0, 0.01, 0.1,
3585                                                               labels, values, G_N_ELEMENTS(labels),
3586                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3587         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3588         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3589         }
3592         /* Trace Background button */
3593         {
3594             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3595                                                           _("Trace Background"),
3596                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3597                                                           "trace_background",
3598                                                           Inkscape::ICON_SIZE_DECORATION );
3599             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3600             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3601             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3602         }
3604         /* Use Pressure button */
3605         {
3606             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3607                                                           _("Pressure"),
3608                                                           _("Use the pressure of the input device to alter the width of the pen"),
3609                                                           "use_pressure",
3610                                                           Inkscape::ICON_SIZE_DECORATION );
3611             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3612             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3613             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3614         }
3616         /* Use Tilt button */
3617         {
3618             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3619                                                           _("Tilt"),
3620                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3621                                                           "use_tilt",
3622                                                           Inkscape::ICON_SIZE_DECORATION );
3623             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3624             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3625             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3626             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3627         }
3629         /*calligraphic profile */
3630         {
3631             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3632             gchar *pref_path;
3634             int max = prefs_get_int_attribute("tools.calligraphic.preset","max_presets",99);
3635             for ( int ii = 0; ii < max; ++ii){
3636                 pref_path = g_strdup_printf("tools.calligraphic.preset.cp%d", ii);
3638                 if ( ! pref_path_exists(pref_path)){
3639                     free(pref_path );
3640                     break;
3641                 }
3643                 gchar const *preset_name = prefs_get_string_attribute(pref_path,"name");
3644                 GtkTreeIter iter;
3645                 gtk_list_store_append( model, &iter );
3646                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ii, -1 );
3647                 free(pref_path );
3648             }
3649             pref_path = NULL;
3650             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", _("Profile"), ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3651             g_object_set( act1, "short_label", _("Profile:"), NULL );
3652             ege_select_one_action_set_appearance( act1, "compact" );
3653             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3654             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3655             g_object_set_data( holder, "channels_action", act1 );
3656         }
3657     }
3661 //########################
3662 //##    Circle / Arc    ##
3663 //########################
3665 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3667     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3668     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3670     if (v1 == 0 && v2 == 0) {
3671         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3672             gtk_action_set_sensitive( ocb, FALSE );
3673             gtk_action_set_sensitive( make_whole, FALSE );
3674         }
3675     } else {
3676         gtk_action_set_sensitive( ocb, TRUE );
3677         gtk_action_set_sensitive( make_whole, TRUE );
3678     }
3681 static void
3682 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3684     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3686     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3687         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3688     }
3690     // quit if run by the attr_changed listener
3691     if (g_object_get_data( tbl, "freeze" )) {
3692         return;
3693     }
3695     // in turn, prevent listener from responding
3696     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3698     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3700     bool modmade = false;
3701     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3702          items != NULL;
3703          items = items->next)
3704     {
3705         SPItem *item = SP_ITEM(items->data);
3707         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3709             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3710             SPArc *arc = SP_ARC(item);
3712             if (!strcmp(value_name, "start"))
3713                 ge->start = (adj->value * M_PI)/ 180;
3714             else
3715                 ge->end = (adj->value * M_PI)/ 180;
3717             sp_genericellipse_normalize(ge);
3718             ((SPObject *)arc)->updateRepr();
3719             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3721             modmade = true;
3722         }
3723     }
3725     g_free(namespaced_name);
3727     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3729     sp_arctb_sensitivize( tbl, adj->value, other->value );
3731     if (modmade) {
3732         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3733                                    _("Arc: Change start/end"));
3734     }
3736     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3740 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3742     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3745 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3747     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3750 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3752     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3753     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3754         if ( ege_select_one_action_get_active( act ) != 0 ) {
3755             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3756         } else {
3757             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3758         }
3759     }
3761     // quit if run by the attr_changed listener
3762     if (g_object_get_data( tbl, "freeze" )) {
3763         return;
3764     }
3766     // in turn, prevent listener from responding
3767     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3769     bool modmade = false;
3771     if ( ege_select_one_action_get_active(act) != 0 ) {
3772         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3773              items != NULL;
3774              items = items->next)
3775         {
3776             if (SP_IS_ARC((SPItem *) items->data)) {
3777                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3778                 repr->setAttribute("sodipodi:open", "true");
3779                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3780                 modmade = true;
3781             }
3782         }
3783     } else {
3784         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3785              items != NULL;
3786              items = items->next)
3787         {
3788             if (SP_IS_ARC((SPItem *) items->data))    {
3789                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3790                 repr->setAttribute("sodipodi:open", NULL);
3791                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3792                 modmade = true;
3793             }
3794         }
3795     }
3797     if (modmade) {
3798         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3799                                    _("Arc: Change open/closed"));
3800     }
3802     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3805 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3807     GtkAdjustment *adj;
3808     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3809     gtk_adjustment_set_value(adj, 0.0);
3810     gtk_adjustment_value_changed(adj);
3812     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3813     gtk_adjustment_set_value(adj, 0.0);
3814     gtk_adjustment_value_changed(adj);
3816     spinbutton_defocus( GTK_OBJECT(obj) );
3819 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3820                                       gchar const */*old_value*/, gchar const */*new_value*/,
3821                                       bool /*is_interactive*/, gpointer data)
3823     GObject *tbl = G_OBJECT(data);
3825     // quit if run by the _changed callbacks
3826     if (g_object_get_data( tbl, "freeze" )) {
3827         return;
3828     }
3830     // in turn, prevent callbacks from responding
3831     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3833     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3834     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3836     GtkAdjustment *adj1,*adj2;
3837     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3838     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3839     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3840     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3842     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3844     char const *openstr = NULL;
3845     openstr = repr->attribute("sodipodi:open");
3846     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3848     if (openstr) {
3849         ege_select_one_action_set_active( ocb, 1 );
3850     } else {
3851         ege_select_one_action_set_active( ocb, 0 );
3852     }
3854     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3857 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3858     NULL, /* child_added */
3859     NULL, /* child_removed */
3860     arc_tb_event_attr_changed,
3861     NULL, /* content_changed */
3862     NULL  /* order_changed */
3863 };
3866 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3868     int n_selected = 0;
3869     Inkscape::XML::Node *repr = NULL;
3871     purge_repr_listener( tbl, tbl );
3873     for (GSList const *items = selection->itemList();
3874          items != NULL;
3875          items = items->next)
3876     {
3877         if (SP_IS_ARC((SPItem *) items->data)) {
3878             n_selected++;
3879             repr = SP_OBJECT_REPR((SPItem *) items->data);
3880         }
3881     }
3883     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3885     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3886     if (n_selected == 0) {
3887         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3888     } else if (n_selected == 1) {
3889         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3890         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3892         if (repr) {
3893             g_object_set_data( tbl, "repr", repr );
3894             Inkscape::GC::anchor(repr);
3895             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3896             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3897         }
3898     } else {
3899         // FIXME: implement averaging of all parameters for multiple selected
3900         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3901         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3902         sp_arctb_sensitivize( tbl, 1, 0 );
3903     }
3907 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3909     EgeAdjustmentAction* eact = 0;
3912     {
3913         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3914         ege_output_action_set_use_markup( act, TRUE );
3915         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3916         g_object_set_data( holder, "mode_action", act );
3917     }
3919     /* Start */
3920     {
3921         eact = create_adjustment_action( "ArcStartAction",
3922                                          _("Start"), _("Start:"),
3923                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3924                                          "tools.shapes.arc", "start", 0.0,
3925                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3926                                          -360.0, 360.0, 1.0, 10.0,
3927                                          0, 0, 0,
3928                                          sp_arctb_start_value_changed);
3929         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3930     }
3932     /* End */
3933     {
3934         eact = create_adjustment_action( "ArcEndAction",
3935                                          _("End"), _("End:"),
3936                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3937                                          "tools.shapes.arc", "end", 0.0,
3938                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3939                                          -360.0, 360.0, 1.0, 10.0,
3940                                          0, 0, 0,
3941                                          sp_arctb_end_value_changed);
3942         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3943     }
3945     /* Segments / Pie checkbox */
3946     {
3947         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3949         GtkTreeIter iter;
3950         gtk_list_store_append( model, &iter );
3951         gtk_list_store_set( model, &iter,
3952                             0, _("Closed arc"),
3953                             1, _("Switch to segment (closed shape with two radii)"),
3954                             2, "circle_closed_arc",
3955                             -1 );
3957         gtk_list_store_append( model, &iter );
3958         gtk_list_store_set( model, &iter,
3959                             0, _("Open Arc"),
3960                             1, _("Switch to arc (unclosed shape)"),
3961                             2, "circle_open_arc",
3962                             -1 );
3964         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3965         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3966         g_object_set_data( holder, "open_action", act );
3968         ege_select_one_action_set_appearance( act, "full" );
3969         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3970         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3971         ege_select_one_action_set_icon_column( act, 2 );
3972         ege_select_one_action_set_tooltip_column( act, 1  );
3974         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3975         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3976         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3977         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3978     }
3980     /* Make Whole */
3981     {
3982         InkAction* inky = ink_action_new( "ArcResetAction",
3983                                           _("Make whole"),
3984                                           _("Make the shape a whole ellipse, not arc or segment"),
3985                                           "reset_circle",
3986                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3987         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3988         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3989         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3990         g_object_set_data( holder, "make_whole", inky );
3991     }
3993     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3994     // sensitivize make whole and open checkbox
3995     {
3996         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3997         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3998         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3999     }
4002     sigc::connection *connection = new sigc::connection(
4003         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4004         );
4005     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4006     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4012 // toggle button callbacks and updaters
4014 //########################
4015 //##      Dropper       ##
4016 //########################
4018 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4019     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4020     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4021     if ( set_action ) {
4022         if ( gtk_toggle_action_get_active( act ) ) {
4023             gtk_action_set_sensitive( set_action, TRUE );
4024         } else {
4025             gtk_action_set_sensitive( set_action, FALSE );
4026         }
4027     }
4029     spinbutton_defocus(GTK_OBJECT(tbl));
4032 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4033     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4034     spinbutton_defocus(GTK_OBJECT(tbl));
4038 /**
4039  * Dropper auxiliary toolbar construction and setup.
4040  *
4041  * TODO: Would like to add swatch of current color.
4042  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4043  *       can drag and drop places. Will provide a nice mixing palette.
4044  */
4045 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4047     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4049     {
4050         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4051         ege_output_action_set_use_markup( act, TRUE );
4052         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4053     }
4055     {
4056         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4057                                                       _("Pick opacity"),
4058                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4059                                                       NULL,
4060                                                       Inkscape::ICON_SIZE_DECORATION );
4061         g_object_set( act, "short_label", _("Pick"), NULL );
4062         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4063         g_object_set_data( holder, "pick_action", act );
4064         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4065         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4066     }
4068     {
4069         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4070                                                       _("Assign opacity"),
4071                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4072                                                       NULL,
4073                                                       Inkscape::ICON_SIZE_DECORATION );
4074         g_object_set( act, "short_label", _("Assign"), NULL );
4075         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4076         g_object_set_data( holder, "set_action", act );
4077         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4078         // make sure it's disabled if we're not picking alpha
4079         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4080         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4081     }
4085 //########################
4086 //##    Text Toolbox    ##
4087 //########################
4088 /*
4089 static void
4090 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4092     //Call back for letter sizing spinbutton
4095 static void
4096 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4098     //Call back for line height spinbutton
4101 static void
4102 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4104     //Call back for horizontal kerning spinbutton
4107 static void
4108 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4110     //Call back for vertical kerning spinbutton
4113 static void
4114 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4116     //Call back for letter rotation spinbutton
4117 }*/
4119 namespace {
4121 bool popdown_visible = false;
4122 bool popdown_hasfocus = false;
4124 void
4125 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4127     SPStyle *query =
4128         sp_style_new (SP_ACTIVE_DOCUMENT);
4130     int result_fontspec =
4131         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4133     int result_family =
4134         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4136     int result_style =
4137         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4139     int result_numbers =
4140         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4142     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4144     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4145     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4146     {
4147         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4149         if (repr)
4150         {
4151             sp_style_read_from_repr (query, repr);
4152         }
4153         else
4154         {
4155             return;
4156         }
4157     }
4159     if (query->text)
4160     {
4161         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4162             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4163             gtk_entry_set_text (GTK_ENTRY (entry), "");
4165         } else if (query->text->font_specification.value || query->text->font_family.value) {
4167             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4169             // Get the font that corresponds
4170             Glib::ustring familyName;
4172             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4173             if (font) {
4174                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4175                 font->Unref();
4176                 font = NULL;
4177             }
4179             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4181             Gtk::TreePath path;
4182             try {
4183                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4184             } catch (...) {
4185                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4186                 return;
4187             }
4189             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4190             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4192             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4194             gtk_tree_selection_select_path (tselection, path.gobj());
4195             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4197             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4198         }
4200         //Size
4201         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4202         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4203         g_object_set_data (tbl, "size-block", gpointer(1));
4204         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4205         g_object_set_data (tbl, "size-block", gpointer(0));
4206         free (str);
4208         //Anchor
4209         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4210         {
4211             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4212             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4213             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4214             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4215         }
4216         else
4217         {
4218             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4219             {
4220                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4221                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4222                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4223                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4224             }
4225             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4226             {
4227                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4228                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4229                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4230                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4231             }
4232             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4233             {
4234                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4235                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4236                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4237                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4238             }
4239         }
4241         //Style
4242         {
4243             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4245             gboolean active = gtk_toggle_button_get_active (button);
4246             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4248             if (active != check)
4249             {
4250                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4251                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4252                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4253             }
4254         }
4256         {
4257             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4259             gboolean active = gtk_toggle_button_get_active (button);
4260             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4262             if (active != check)
4263             {
4264                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4265                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4266                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4267             }
4268         }
4270         //Orientation
4271         //locking both buttons, changing one affect all group (both)
4272         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4273         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4275         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4276         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4278         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4279         {
4280             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4281         }
4282         else
4283         {
4284             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4285         }
4286         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4287         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4288     }
4290     sp_style_unref(query);
4293 void
4294 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4296     sp_text_toolbox_selection_changed (selection, tbl);
4299 void
4300 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4302     sp_text_toolbox_selection_changed (NULL, tbl);
4305 void
4306 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4307                                 GObject             *tbl)
4309     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4310     GtkTreeModel *model = 0;
4311     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4312     GtkTreeIter   iter;
4313     char         *family = 0;
4315     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4316     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4318     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4319         return;
4320     }
4322     gtk_tree_model_get (model, &iter, 0, &family, -1);
4324     if (g_object_get_data (G_OBJECT (selection), "block"))
4325     {
4326         gtk_entry_set_text (GTK_ENTRY (entry), family);
4327         return;
4328     }
4330     gtk_entry_set_text (GTK_ENTRY (entry), family);
4332     SPStyle *query =
4333         sp_style_new (SP_ACTIVE_DOCUMENT);
4335     int result_fontspec =
4336         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4338     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4340     SPCSSAttr *css = sp_repr_css_attr_new ();
4343     // First try to get the font spec from the stored value
4344     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4346     if (fontSpec.empty()) {
4347         // Construct a new font specification if it does not yet exist
4348         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4349         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4350         fontFromStyle->Unref();
4351     }
4353     if (!fontSpec.empty()) {
4354         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4355         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4356             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4357             if (font) {
4358                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4360                 // Set all the these just in case they were altered when finding the best
4361                 // match for the new family and old style...
4363                 gchar c[256];
4365                 font->Family(c, 256);
4366                 sp_repr_css_set_property (css, "font-family", c);
4368                 font->Attribute( "weight", c, 256);
4369                 sp_repr_css_set_property (css, "font-weight", c);
4371                 font->Attribute("style", c, 256);
4372                 sp_repr_css_set_property (css, "font-style", c);
4374                 font->Attribute("stretch", c, 256);
4375                 sp_repr_css_set_property (css, "font-stretch", c);
4377                 font->Attribute("variant", c, 256);
4378                 sp_repr_css_set_property (css, "font-variant", c);
4380                 font->Unref();
4381             }
4382         }
4383     }
4385     // If querying returned nothing, set the default style of the tool (for new texts)
4386     if (result_fontspec == QUERY_STYLE_NOTHING)
4387     {
4388         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4389         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4390     }
4391     else
4392     {
4393         sp_desktop_set_style (desktop, css, true, true);
4394     }
4396     sp_style_unref(query);
4398     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4399                                    _("Text: Change font family"));
4400     sp_repr_css_attr_unref (css);
4401     free (family);
4402     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4404     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4407 void
4408 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4409                                        GObject      *tbl)
4411     const char *family = gtk_entry_get_text (entry);
4413     try {
4414         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4415         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4416         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4417         gtk_tree_selection_select_path (selection, path.gobj());
4418         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4419         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4420     } catch (...) {
4421         if (family && strlen (family))
4422         {
4423             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4424         }
4425     }
4428 void
4429 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4430                                    gpointer          data)
4432     if (g_object_get_data (G_OBJECT (button), "block")) return;
4433     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4434     int prop = GPOINTER_TO_INT(data);
4436     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4437     SPCSSAttr *css = sp_repr_css_attr_new ();
4439     switch (prop)
4440     {
4441         case 0:
4442         {
4443             sp_repr_css_set_property (css, "text-anchor", "start");
4444             sp_repr_css_set_property (css, "text-align", "start");
4445             break;
4446         }
4447         case 1:
4448         {
4449             sp_repr_css_set_property (css, "text-anchor", "middle");
4450             sp_repr_css_set_property (css, "text-align", "center");
4451             break;
4452         }
4454         case 2:
4455         {
4456             sp_repr_css_set_property (css, "text-anchor", "end");
4457             sp_repr_css_set_property (css, "text-align", "end");
4458             break;
4459         }
4461         case 3:
4462         {
4463             sp_repr_css_set_property (css, "text-anchor", "start");
4464             sp_repr_css_set_property (css, "text-align", "justify");
4465             break;
4466         }
4467     }
4469     SPStyle *query =
4470         sp_style_new (SP_ACTIVE_DOCUMENT);
4471     int result_numbers =
4472         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4474     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4475     if (result_numbers == QUERY_STYLE_NOTHING)
4476     {
4477         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4478     }
4480     sp_style_unref(query);
4482     sp_desktop_set_style (desktop, css, true, true);
4483     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4484                                    _("Text: Change alignment"));
4485     sp_repr_css_attr_unref (css);
4487     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4490 void
4491 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4492                                gpointer          data)
4494     if (g_object_get_data (G_OBJECT (button), "block")) return;
4496     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4497     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4498     int          prop       = GPOINTER_TO_INT(data);
4499     bool         active     = gtk_toggle_button_get_active (button);
4501     SPStyle *query =
4502         sp_style_new (SP_ACTIVE_DOCUMENT);
4504     int result_fontspec =
4505         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4507     int result_family =
4508         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4510     int result_style =
4511         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4513     int result_numbers =
4514         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4516     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4517     Glib::ustring newFontSpec = "";
4519     if (fontSpec.empty()) {
4520         // Construct a new font specification if it does not yet exist
4521         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4522         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4523         fontFromStyle->Unref();
4524     }
4526     switch (prop)
4527     {
4528         case 0:
4529         {
4530             if (!fontSpec.empty()) {
4531                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4532             }
4533             if (fontSpec != newFontSpec) {
4534                 // Don't even set the bold if the font didn't exist on the system
4535                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4536             }
4537             break;
4538         }
4540         case 1:
4541         {
4542             if (!fontSpec.empty()) {
4543                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4544             }
4545             if (fontSpec != newFontSpec) {
4546                 // Don't even set the italic if the font didn't exist on the system
4547                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4548             }
4549             break;
4550         }
4551     }
4553     if (!newFontSpec.empty()) {
4554         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4555     }
4557     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4558     if (result_fontspec == QUERY_STYLE_NOTHING)
4559     {
4560         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4561     }
4563     sp_style_unref(query);
4565     sp_desktop_set_style (desktop, css, true, true);
4566     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4567                                    _("Text: Change font style"));
4568     sp_repr_css_attr_unref (css);
4570     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4573 void
4574 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4575                                      gpointer         data)
4577     if (g_object_get_data (G_OBJECT (button), "block")) {
4578         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4579         return;
4580     }
4582     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4583     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4584     int          prop       = GPOINTER_TO_INT(data);
4586     switch (prop)
4587     {
4588         case 0:
4589         {
4590             sp_repr_css_set_property (css, "writing-mode", "lr");
4591             break;
4592         }
4594         case 1:
4595         {
4596             sp_repr_css_set_property (css, "writing-mode", "tb");
4597             break;
4598         }
4599     }
4601     SPStyle *query =
4602         sp_style_new (SP_ACTIVE_DOCUMENT);
4603     int result_numbers =
4604         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4606     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4607     if (result_numbers == QUERY_STYLE_NOTHING)
4608     {
4609         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4610     }
4612     sp_desktop_set_style (desktop, css, true, true);
4613     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4614                                    _("Text: Change orientation"));
4615     sp_repr_css_attr_unref (css);
4617     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4620 gboolean
4621 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4623     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4624     if (!desktop) return FALSE;
4626     switch (get_group0_keyval (event)) {
4627         case GDK_Escape: // defocus
4628             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4629             sp_text_toolbox_selection_changed (NULL, tbl); // update
4630             return TRUE; // I consumed the event
4631             break;
4632     }
4633     return FALSE;
4636 gboolean
4637 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4639     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4640     if (!desktop) return FALSE;
4642     switch (get_group0_keyval (event)) {
4643         case GDK_KP_Enter:
4644         case GDK_Return:
4645         case GDK_Escape: // defocus
4646             gtk_widget_hide (w);
4647             popdown_visible = false;
4648             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4649             return TRUE; // I consumed the event
4650             break;
4651         case GDK_w:
4652         case GDK_W:
4653             if (event->state & GDK_CONTROL_MASK) {
4654                 gtk_widget_hide (w);
4655                 popdown_visible = false;
4656                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4657                 return TRUE; // I consumed the event
4658             }
4659             break;
4660     }
4661     return FALSE;
4665 void
4666 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4667                                GObject     *tbl)
4669     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4671     if (g_object_get_data (tbl, "size-block")) return;
4673     // If this is not from selecting a size in the list (in which case get_active will give the
4674     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4675     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4676     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4677     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4678         return;
4680     gchar *endptr;
4681     gdouble value = -1;
4682     char *text = gtk_combo_box_get_active_text (cbox);
4683     if (text) {
4684         value = g_strtod (text, &endptr);
4685         if (endptr == text) // conversion failed, non-numeric input
4686             value = -1;
4687         free (text);
4688     }
4689     if (value <= 0) {
4690         return; // could not parse value
4691     }
4693     SPCSSAttr *css = sp_repr_css_attr_new ();
4694     Inkscape::CSSOStringStream osfs;
4695     osfs << value;
4696     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4698     SPStyle *query =
4699         sp_style_new (SP_ACTIVE_DOCUMENT);
4700     int result_numbers =
4701         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4703     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4704     if (result_numbers == QUERY_STYLE_NOTHING)
4705     {
4706         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4707     }
4709     sp_style_unref(query);
4711     sp_desktop_set_style (desktop, css, true, true);
4712     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4713                                    _("Text: Change font size"));
4714     sp_repr_css_attr_unref (css);
4716     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4719 gboolean
4720 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4722     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4723     if (!desktop) return FALSE;
4725     if (!g_object_get_data (tbl, "esc-pressed")) {
4726         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4727         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4728         sp_text_toolbox_size_changed (cbox, tbl);
4729         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4730     }
4731     return FALSE; // I consumed the event
4735 gboolean
4736 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4738     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4739     if (!desktop) return FALSE;
4741     switch (get_group0_keyval (event)) {
4742         case GDK_Escape: // defocus
4743             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4744             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4745             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4746             return TRUE; // I consumed the event
4747             break;
4748         case GDK_Return: // defocus
4749         case GDK_KP_Enter:
4750             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4751             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4752             sp_text_toolbox_size_changed (cbox, tbl);
4753             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4754             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4755             return TRUE; // I consumed the event
4756             break;
4757     }
4758     return FALSE;
4761 void
4762 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4763                                          GObject            *tbl)
4765     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4766     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4767     int x, y;
4769     if (!popdown_visible)
4770     {
4771         gdk_window_get_origin (widget->window, &x, &y);
4772         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4773         gtk_widget_show_all (popdown);
4774         //sp_transientize (popdown);
4776         gdk_pointer_grab (widget->window, TRUE,
4777                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4778                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4779                                         GDK_POINTER_MOTION_MASK),
4780                           NULL, NULL, GDK_CURRENT_TIME);
4782         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4784         popdown_visible = true;
4785     }
4786     else
4787     {
4788         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4789         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4790         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4791         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4792         gtk_widget_hide (popdown);
4793         popdown_visible = false;
4794     }
4797 gboolean
4798 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4799                                  GdkEventFocus    */*event*/,
4800                                  GObject          */*tbl*/)
4802     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4803     return FALSE;
4806 gboolean
4807 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4808                                    GdkEventFocus    */*event*/,
4809                                    GObject          */*tbl*/)
4811     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4813     if (popdown_hasfocus) {
4814         gtk_widget_hide (popdown);
4815         popdown_hasfocus = false;
4816         popdown_visible = false;
4817         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4818         return TRUE;
4819     }
4820     return FALSE;
4823 gboolean
4824 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
4825                                    GdkEventFocus    */*event*/,
4826                                    GObject          */*tbl*/)
4828     popdown_hasfocus = true;
4829     return TRUE;
4833 void
4834 cell_data_func  (GtkTreeViewColumn */*column*/,
4835                  GtkCellRenderer   *cell,
4836                  GtkTreeModel      *tree_model,
4837                  GtkTreeIter       *iter,
4838                  gpointer           /*data*/)
4840     char        *family,
4841         *family_escaped,
4842         *sample_escaped;
4844     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4846     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4848     family_escaped = g_markup_escape_text (family, -1);
4849     sample_escaped = g_markup_escape_text (sample, -1);
4851     std::stringstream markup;
4852     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4853     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4855     free (family);
4856     free (family_escaped);
4857     free (sample_escaped);
4860 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4861     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4862     if (completion) {
4863         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4864         g_object_unref (completion);
4865     }
4868 GtkWidget*
4869 sp_text_toolbox_new (SPDesktop *desktop)
4871     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4873     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4874     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4876     GtkTooltips *tt = gtk_tooltips_new();
4877     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4879     ////////////Family
4880     //Window
4881     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4882     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4884     //Entry
4885     GtkWidget           *entry = gtk_entry_new ();
4886     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4887     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4888     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4889     gtk_entry_completion_set_text_column (completion, 0);
4890     gtk_entry_completion_set_minimum_key_length (completion, 1);
4891     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4892     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4893     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4894     aux_toolbox_space (tbl, 1);
4895     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4896     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4898     //Button
4899     GtkWidget   *button = gtk_button_new ();
4900     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4901     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4903     //Popdown
4904     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4905     GtkWidget           *treeview = gtk_tree_view_new ();
4907     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4908     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4909     gtk_tree_view_column_pack_start (column, cell, FALSE);
4910     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4911     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4912     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4914     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4915     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4916     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4918     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4920     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4921     gtk_container_add (GTK_CONTAINER (sw), treeview);
4923     gtk_container_add (GTK_CONTAINER (window), sw);
4924     gtk_widget_set_size_request (window, 300, 450);
4926     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4927     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4928     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4930     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4932     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4933     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
4934     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4936     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4937     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4939     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4940     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4941     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4942     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4943     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4945     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4946     aux_toolbox_space (tbl, 1);
4947     GtkWidget *box = gtk_event_box_new ();
4948     gtk_container_add (GTK_CONTAINER (box), image);
4949     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4950     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4951     GtkTooltips *tooltips = gtk_tooltips_new ();
4952     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4953     gtk_widget_hide (GTK_WIDGET (box));
4954     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4956     ////////////Size
4957     const char *sizes[] = {
4958         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4959         "16", "18", "20", "22", "24", "28",
4960         "32", "36", "40", "48", "56", "64", "72", "144"
4961     };
4963     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4964     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4965     gtk_widget_set_size_request (cbox, 80, -1);
4966     aux_toolbox_space (tbl, 1);
4967     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4968     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4969     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4970     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4971     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4973     //spacer
4974     aux_toolbox_space (tbl, 4);
4975     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4977     ////////////Text anchor
4978     GtkWidget *group   = gtk_radio_button_new (NULL);
4979     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4980     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4982     // left
4983     GtkWidget *rbutton = group;
4984     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4985     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4986     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4988     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4989     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4990     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4991     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4993     // center
4994     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4995     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4996     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4997     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4999     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5000     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5001     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5002     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5004     // right
5005     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5006     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5007     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5008     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5010     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5011     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5012     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5013     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5015     // fill
5016     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5017     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5018     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5019     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5021     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5022     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5023     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5024     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5026     aux_toolbox_space (tbl, 1);
5027     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5029     //spacer
5030     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5032     ////////////Text style
5033     row = gtk_hbox_new (FALSE, 4);
5035     // bold
5036     rbutton = gtk_toggle_button_new ();
5037     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5038     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5039     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5040     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5042     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5043     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5044     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5046     // italic
5047     rbutton = gtk_toggle_button_new ();
5048     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5049     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5050     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5051     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5053     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5054     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5055     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5057     aux_toolbox_space (tbl, 1);
5058     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5060     //spacer
5061     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5063     ////////////Text orientation
5064     group   = gtk_radio_button_new (NULL);
5065     row     = gtk_hbox_new (FALSE, 4);
5066     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5068     // horizontal
5069     rbutton = group;
5070     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5071     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5072     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5073     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5075     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5076     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5077     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5079     // vertical
5080     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5081     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5082     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5083     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5084     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5086     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5087     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5088     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5089     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5092     //watch selection
5093     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5095     sigc::connection *c_selection_changed =
5096         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5097                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5098     pool->add_connection ("selection-changed", c_selection_changed);
5100     sigc::connection *c_selection_modified =
5101         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5102                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5103     pool->add_connection ("selection-modified", c_selection_modified);
5105     sigc::connection *c_subselection_changed =
5106         new sigc::connection (desktop->connectToolSubselectionChanged
5107                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5108     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5110     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5113     gtk_widget_show_all (tbl);
5114     return tbl;
5116 } // end of sp_text_toolbox_new()
5118 }//<unnamed> namespace
5121 //#########################
5122 //##      Connector      ##
5123 //#########################
5125 static void sp_connector_path_set_avoid(void)
5127     cc_selection_set_avoid(true);
5131 static void sp_connector_path_set_ignore(void)
5133     cc_selection_set_avoid(false);
5138 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5140     // quit if run by the _changed callbacks
5141     if (g_object_get_data( tbl, "freeze" )) {
5142         return;
5143     }
5145     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5146     SPDocument *doc = sp_desktop_document(desktop);
5148     if (!sp_document_get_undo_sensitive(doc))
5149     {
5150         return;
5151     }
5153     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5155     if ( repr->attribute("inkscape:connector-spacing") ) {
5156         gdouble priorValue = gtk_adjustment_get_value(adj);
5157         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5158         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5159             return;
5160         }
5161     } else if ( adj->value == defaultConnSpacing ) {
5162         return;
5163     }
5165     // in turn, prevent callbacks from responding
5166     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5168     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5169     SP_OBJECT(desktop->namedview)->updateRepr();
5171     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5172     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5173         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5174         NR::Matrix m = NR::identity();
5175         avoid_item_move(&m, item);
5176     }
5178     if (items) {
5179         g_slist_free(items);
5180     }
5182     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5183             _("Change connector spacing"));
5185     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5187     spinbutton_defocus(GTK_OBJECT(tbl));
5190 static void sp_connector_graph_layout(void)
5192     if (!SP_ACTIVE_DESKTOP) return;
5194     // hack for clones, see comment in align-and-distribute.cpp
5195     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5196     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5198     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5200     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5202     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5205 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5207     if ( gtk_toggle_action_get_active( act ) ) {
5208         prefs_set_string_attribute("tools.connector", "directedlayout",
5209                 "true");
5210     } else {
5211         prefs_set_string_attribute("tools.connector", "directedlayout",
5212                 "false");
5213     }
5216 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5218     if ( gtk_toggle_action_get_active( act ) ) {
5219         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5220                 "true");
5221     } else {
5222         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5223                 "false");
5224     }
5228 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5230     prefs_set_double_attribute("tools.connector", "length", adj->value);
5231     spinbutton_defocus(GTK_OBJECT(tbl));
5234 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5235                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5236                                             bool /*is_interactive*/, gpointer data)
5238     GtkWidget *tbl = GTK_WIDGET(data);
5240     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5241         return;
5242     }
5243     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5244         return;
5245     }
5247     GtkAdjustment *adj = (GtkAdjustment*)
5248             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5249     gdouble spacing = defaultConnSpacing;
5250     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5252     gtk_adjustment_set_value(adj, spacing);
5256 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5257     NULL, /* child_added */
5258     NULL, /* child_removed */
5259     connector_tb_event_attr_changed,
5260     NULL, /* content_changed */
5261     NULL  /* order_changed */
5262 };
5265 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5267     {
5268         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5269                                           _("Avoid"),
5270                                           _("Make connectors avoid selected objects"),
5271                                           "connector_avoid",
5272                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5273         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5274         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5275     }
5277     {
5278         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5279                                           _("Ignore"),
5280                                           _("Make connectors ignore selected objects"),
5281                                           "connector_ignore",
5282                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5283         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5284         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5285     }
5287     EgeAdjustmentAction* eact = 0;
5289     // Spacing spinbox
5290     eact = create_adjustment_action( "ConnectorSpacingAction",
5291                                      _("Connector Spacing"), _("Spacing:"),
5292                                      _("The amount of space left around objects by auto-routing connectors"),
5293                                      "tools.connector", "spacing", defaultConnSpacing,
5294                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5295                                      0, 100, 1.0, 10.0,
5296                                      0, 0, 0,
5297                                      connector_spacing_changed, 1, 0 );
5298     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5300     // Graph (connector network) layout
5301     {
5302         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5303                                           _("Graph"),
5304                                           _("Nicely arrange selected connector network"),
5305                                           "graph_layout",
5306                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5307         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5308         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5309     }
5311     // Default connector length spinbox
5312     eact = create_adjustment_action( "ConnectorLengthAction",
5313                                      _("Connector Length"), _("Length:"),
5314                                      _("Ideal length for connectors when layout is applied"),
5315                                      "tools.connector", "length", 100,
5316                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5317                                      10, 1000, 10.0, 100.0,
5318                                      0, 0, 0,
5319                                      connector_length_changed, 1, 0 );
5320     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5323     // Directed edges toggle button
5324     {
5325         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5326                                                       _("Downwards"),
5327                                                       _("Make connectors with end-markers (arrows) point downwards"),
5328                                                       "directed_graph",
5329                                                       Inkscape::ICON_SIZE_DECORATION );
5330         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5332         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5333         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5334                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5336         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5337     }
5339     // Avoid overlaps toggle button
5340     {
5341         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5342                                                       _("Remove overlaps"),
5343                                                       _("Do not allow overlapping shapes"),
5344                                                       "remove_overlaps",
5345                                                       Inkscape::ICON_SIZE_DECORATION );
5346         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5348         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5349         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5350                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5352         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5353     }
5355     // Code to watch for changes to the connector-spacing attribute in
5356     // the XML.
5357     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5358     g_assert(repr != NULL);
5360     purge_repr_listener( holder, holder );
5362     if (repr) {
5363         g_object_set_data( holder, "repr", repr );
5364         Inkscape::GC::anchor(repr);
5365         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5366         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5367     }
5368 } // end of sp_connector_toolbox_prep()
5371 //#########################
5372 //##     Paintbucket     ##
5373 //#########################
5375 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5377     gint channels = ege_select_one_action_get_active( act );
5378     flood_channels_set_channels( channels );
5381 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5383     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5386 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5388     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5391 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5393     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5394     SPUnit const *unit = tracker->getActiveUnit();
5396     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5398     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5401 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5403     // FIXME: make defaults settable via Inkscape Options
5404     struct KeyValue {
5405         char const *key;
5406         double value;
5407     } const key_values[] = {
5408         {"threshold", 15},
5409         {"offset", 0.0}
5410     };
5412     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5413         KeyValue const &kv = key_values[i];
5414         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5415         if ( adj ) {
5416             gtk_adjustment_set_value(adj, kv.value);
5417         }
5418     }
5420     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5421     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5422     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5423     ege_select_one_action_set_active( autogap_action, 0 );
5426 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5428     EgeAdjustmentAction* eact = 0;
5430     {
5431         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5433         GList* items = 0;
5434         gint count = 0;
5435         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5436         {
5437             GtkTreeIter iter;
5438             gtk_list_store_append( model, &iter );
5439             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5440             count++;
5441         }
5442         g_list_free( items );
5443         items = 0;
5444         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5445         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5446         ege_select_one_action_set_appearance( act1, "compact" );
5447         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5448         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5449         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5450         g_object_set_data( holder, "channels_action", act1 );
5451     }
5453     // Spacing spinbox
5454     {
5455         eact = create_adjustment_action(
5456             "ThresholdAction",
5457             _("Fill Threshold"), _("Threshold:"),
5458             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5459             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5460             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5461             0, 0, 0,
5462             paintbucket_threshold_changed, 1, 0 );
5464         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5465     }
5467     // Create the units menu.
5468     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5469     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5470     if (stored_unit)
5471         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5472     g_object_set_data( holder, "tracker", tracker );
5473     {
5474         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5475         gtk_action_group_add_action( mainActions, act );
5476     }
5478     // Offset spinbox
5479     {
5480         eact = create_adjustment_action(
5481             "OffsetAction",
5482             _("Grow/shrink by"), _("Grow/shrink by:"),
5483             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5484             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5485             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5486             0, 0, 0,
5487             paintbucket_offset_changed, 1, 2);
5488         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5490         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5491     }
5493     /* Auto Gap */
5494     {
5495         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5497         GList* items = 0;
5498         gint count = 0;
5499         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5500         {
5501             GtkTreeIter iter;
5502             gtk_list_store_append( model, &iter );
5503             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5504             count++;
5505         }
5506         g_list_free( items );
5507         items = 0;
5508         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5509         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5510         ege_select_one_action_set_appearance( act2, "compact" );
5511         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5512         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5513         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5514         g_object_set_data( holder, "autogap_action", act2 );
5515     }
5517     /* Reset */
5518     {
5519         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5520                                           _("Defaults"),
5521                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5522                                           GTK_STOCK_CLEAR );
5523         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5524         gtk_action_group_add_action( mainActions, act );
5525         gtk_action_set_sensitive( act, TRUE );
5526     }
5530 /*
5531   Local Variables:
5532   mode:c++
5533   c-file-style:"stroustrup"
5534   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5535   indent-tabs-mode:nil
5536   fill-column:99
5537   End:
5538 */
5539 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :