Code

Enable 3D box toolbar
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <gtkmm.h>
31 #include <gtk/gtk.h>
32 #include <iostream>
33 #include <sstream>
35 #include "widgets/button.h"
36 #include "widgets/widget-sizes.h"
37 #include "widgets/spw-utilities.h"
38 #include "widgets/spinbutton-events.h"
39 #include "dialogs/text-edit.h"
41 #include "ui/widget/style-swatch.h"
43 #include "prefs-utils.h"
44 #include "verbs.h"
45 #include "sp-namedview.h"
46 #include "desktop.h"
47 #include "desktop-handles.h"
48 #include "xml/repr.h"
49 #include "xml/node-event-vector.h"
50 #include <glibmm/i18n.h>
51 #include "helper/unit-menu.h"
52 #include "helper/units.h"
54 #include "inkscape.h"
55 #include "conn-avoid-ref.h"
58 #include "select-toolbar.h"
59 #include "gradient-toolbar.h"
61 #include "connector-context.h"
62 #include "node-context.h"
63 #include "shape-editor.h"
64 #include "tweak-context.h"
65 #include "sp-rect.h"
66 #include "box3d.h"
67 #include "box3d-context.h"
68 #include "sp-star.h"
69 #include "sp-spiral.h"
70 #include "sp-ellipse.h"
71 #include "sp-text.h"
72 #include "sp-flowtext.h"
73 #include "style.h"
74 #include "selection.h"
75 #include "selection-chemistry.h"
76 #include "document-private.h"
77 #include "desktop-style.h"
78 #include "../libnrtype/font-lister.h"
79 #include "../libnrtype/font-instance.h"
80 #include "../connection-pool.h"
81 #include "../prefs-utils.h"
82 #include "../inkscape-stock.h"
83 #include "icon.h"
84 #include "graphlayout/graphlayout.h"
86 #include "mod360.h"
88 #include "toolbox.h"
90 #include "flood-context.h"
92 #include "ink-action.h"
93 #include "ege-adjustment-action.h"
94 #include "ege-output-action.h"
95 #include "ege-select-one-action.h"
96 #include "helper/unit-tracker.h"
98 using Inkscape::UnitTracker;
100 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
101 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
103 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
116 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
122 static struct {
123     gchar const *type_name;
124     gchar const *data_name;
125     sp_verb_t verb;
126     sp_verb_t doubleclick_verb;
127 } const tools[] = {
128     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
129     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
130     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
131     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
132     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
133     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
134     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
135     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
136     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
137     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
138     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
139     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
140     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
141     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
142     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
143     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
144     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
145     { NULL, NULL, 0, 0 }
146 };
148 static struct {
149     gchar const *type_name;
150     gchar const *data_name;
151     GtkWidget *(*create_func)(SPDesktop *desktop);
152     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
153     gchar const *ui_name;
154     gint swatch_verb_id;
155     gchar const *swatch_tool;
156     gchar const *swatch_tip;
157 } const aux_toolboxes[] = {
158     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
159       SP_VERB_INVALID, 0, 0},
160     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
161       SP_VERB_INVALID, 0, 0},
162     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
163       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
164     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
165       SP_VERB_INVALID, 0, 0},
166     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
167       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
168     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
169       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
170     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
171       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
172     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
173       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
174     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
175       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
176     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
177       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
178     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
179       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
180     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
181       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
182     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
183       SP_VERB_INVALID, 0, 0},
184     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
187       SP_VERB_INVALID, 0, 0},
188     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
189       SP_VERB_INVALID, 0, 0},
190     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
191       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
192     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
193 };
196 static gchar const * ui_descr =
197         "<ui>"
198         "  <toolbar name='SelectToolbar'>"
199         "    <toolitem action='EditSelectAll' />"
200         "    <toolitem action='EditSelectAllInAllLayers' />"
201         "    <toolitem action='EditDeselect' />"
202         "    <separator />"
203         "    <toolitem action='ObjectRotate90CCW' />"
204         "    <toolitem action='ObjectRotate90' />"
205         "    <toolitem action='ObjectFlipHorizontally' />"
206         "    <toolitem action='ObjectFlipVertically' />"
207         "    <separator />"
208         "    <toolitem action='SelectionToBack' />"
209         "    <toolitem action='SelectionLower' />"
210         "    <toolitem action='SelectionRaise' />"
211         "    <toolitem action='SelectionToFront' />"
212         "    <separator />"
213         "    <toolitem action='XAction' />"
214         "    <toolitem action='YAction' />"
215         "    <toolitem action='WidthAction' />"
216         "    <toolitem action='LockAction' />"
217         "    <toolitem action='HeightAction' />"
218         "    <toolitem action='UnitsAction' />"
219         "    <separator />"
220         "    <toolitem action='transform_affect_label' />"
221         "    <toolitem action='transform_stroke' />"
222         "    <toolitem action='transform_corners' />"
223         "    <toolitem action='transform_gradient' />"
224         "    <toolitem action='transform_pattern' />"
225         "  </toolbar>"
227         "  <toolbar name='NodeToolbar'>"
228         "    <toolitem action='NodeInsertAction' />"
229         "    <toolitem action='NodeDeleteAction' />"
230         "    <separator />"
231         "    <toolitem action='NodeJoinAction' />"
232         "    <toolitem action='NodeJoinSegmentAction' />"
233         "    <toolitem action='NodeDeleteSegmentAction' />"
234         "    <toolitem action='NodeBreakAction' />"
235         "    <separator />"
236         "    <toolitem action='NodeCuspAction' />"
237         "    <toolitem action='NodeSmoothAction' />"
238         "    <toolitem action='NodeSymmetricAction' />"
239         "    <separator />"
240         "    <toolitem action='NodeLineAction' />"
241         "    <toolitem action='NodeCurveAction' />"
242         "    <separator />"
243         "    <toolitem action='ObjectToPath' />"
244         "    <toolitem action='StrokeToPath' />"
245         "    <separator />"
246         "    <toolitem action='NodesShowHandlesAction' />"
247         "    <separator />"
248         "    <toolitem action='EditNextLPEParameterAction' />"
249         "    <separator />"
250         "    <toolitem action='NodeXAction' />"
251         "    <toolitem action='NodeYAction' />"
252         "    <toolitem action='NodeUnitsAction' />"
253         "  </toolbar>"
255         "  <toolbar name='TweakToolbar'>"
256         "    <toolitem action='TweakWidthAction' />"
257         "    <separator />"
258         "    <toolitem action='TweakForceAction' />"
259         "    <toolitem action='TweakPressureAction' />"
260         "    <separator />"
261         "    <toolitem action='TweakModeAction' />"
262         "    <separator />"
263         "    <toolitem action='TweakFidelityAction' />"
264         "    <separator />"
265         "    <toolitem action='TweakChannelsLabel' />"
266         "    <toolitem action='TweakDoH' />"
267         "    <toolitem action='TweakDoS' />"
268         "    <toolitem action='TweakDoL' />"
269         "    <toolitem action='TweakDoO' />"
270         "  </toolbar>"
272         "  <toolbar name='ZoomToolbar'>"
273         "    <toolitem action='ZoomIn' />"
274         "    <toolitem action='ZoomOut' />"
275         "    <separator />"
276         "    <toolitem action='Zoom1:0' />"
277         "    <toolitem action='Zoom1:2' />"
278         "    <toolitem action='Zoom2:1' />"
279         "    <separator />"
280         "    <toolitem action='ZoomSelection' />"
281         "    <toolitem action='ZoomDrawing' />"
282         "    <toolitem action='ZoomPage' />"
283         "    <toolitem action='ZoomPageWidth' />"
284         "    <separator />"
285         "    <toolitem action='ZoomPrev' />"
286         "    <toolitem action='ZoomNext' />"
287         "  </toolbar>"
289         "  <toolbar name='StarToolbar'>"
290         "    <separator />"
291         "    <toolitem action='StarStateAction' />"
292         "    <separator />"
293         "    <toolitem action='FlatAction' />"
294         "    <separator />"
295         "    <toolitem action='MagnitudeAction' />"
296         "    <toolitem action='SpokeAction' />"
297         "    <toolitem action='RoundednessAction' />"
298         "    <toolitem action='RandomizationAction' />"
299         "    <separator />"
300         "    <toolitem action='StarResetAction' />"
301         "  </toolbar>"
303         "  <toolbar name='RectToolbar'>"
304         "    <toolitem action='RectStateAction' />"
305         "    <toolitem action='RectWidthAction' />"
306         "    <toolitem action='RectHeightAction' />"
307         "    <toolitem action='RadiusXAction' />"
308         "    <toolitem action='RadiusYAction' />"
309         "    <toolitem action='RectUnitsAction' />"
310         "    <separator />"
311         "    <toolitem action='RectResetAction' />"
312         "  </toolbar>"
314         "  <toolbar name='3DBoxToolbar'>"
315         "    <toolitem action='3DBoxAngleXAction' />"
316         "    <toolitem action='3DBoxVPXStateAction' />"
317         "    <separator />"
318         "    <toolitem action='3DBoxAngleYAction' />"
319         "    <toolitem action='3DBoxVPYStateAction' />"
320         "    <separator />"
321         "    <toolitem action='3DBoxAngleZAction' />"
322         "    <toolitem action='3DBoxVPZStateAction' />"
323         "  </toolbar>"
325         "  <toolbar name='SpiralToolbar'>"
326         "    <toolitem action='SpiralStateAction' />"
327         "    <toolitem action='SpiralRevolutionAction' />"
328         "    <toolitem action='SpiralExpansionAction' />"
329         "    <toolitem action='SpiralT0Action' />"
330         "    <separator />"
331         "    <toolitem action='SpiralResetAction' />"
332         "  </toolbar>"
334         "  <toolbar name='PenToolbar'>"
335         "  </toolbar>"
337         "  <toolbar name='PencilToolbar'>"
338         "  </toolbar>"
340         "  <toolbar name='CalligraphyToolbar'>"
341         "    <separator />"
342         "    <toolitem action='CalligraphyWidthAction' />"
343         "    <toolitem action='PressureAction' />"
344         "    <toolitem action='TraceAction' />"
345         "    <toolitem action='ThinningAction' />"
346         "    <separator />"
347         "    <toolitem action='AngleAction' />"
348         "    <toolitem action='TiltAction' />"
349         "    <toolitem action='FixationAction' />"
350         "    <separator />"
351         "    <toolitem action='CapRoundingAction' />"
352         "    <separator />"
353         "    <toolitem action='TremorAction' />"
354         "    <toolitem action='WiggleAction' />"
355         "    <toolitem action='MassAction' />"
356         "    <separator />"
357         "    <toolitem action='CalligraphyResetAction' />"
358         "  </toolbar>"
360         "  <toolbar name='ArcToolbar'>"
361         "    <toolitem action='ArcStateAction' />"
362         "    <separator />"
363         "    <toolitem action='ArcStartAction' />"
364         "    <toolitem action='ArcEndAction' />"
365         "    <separator />"
366         "    <toolitem action='ArcOpenAction' />"
367         "    <separator />"
368         "    <toolitem action='ArcResetAction' />"
369         "    <separator />"
370         "  </toolbar>"
372         "  <toolbar name='PaintbucketToolbar'>"
373         "    <toolitem action='ChannelsAction' />"
374         "    <separator />"
375         "    <toolitem action='ThresholdAction' />"
376         "    <separator />"
377         "    <toolitem action='OffsetAction' />"
378         "    <toolitem action='PaintbucketUnitsAction' />"
379         "    <separator />"
380         "    <toolitem action='AutoGapAction' />"
381         "    <separator />"
382         "    <toolitem action='PaintbucketResetAction' />"
383         "  </toolbar>"
385         "  <toolbar name='DropperToolbar'>"
386         "    <toolitem action='DropperPickAlphaAction' />"
387         "    <toolitem action='DropperSetAlphaAction' />"
388         "  </toolbar>"
390         "  <toolbar name='ConnectorToolbar'>"
391         "    <toolitem action='ConnectorAvoidAction' />"
392         "    <toolitem action='ConnectorIgnoreAction' />"
393         "    <toolitem action='ConnectorSpacingAction' />"
394         "    <toolitem action='ConnectorGraphAction' />"
395         "    <toolitem action='ConnectorLengthAction' />"
396         "    <toolitem action='ConnectorDirectedAction' />"
397         "    <toolitem action='ConnectorOverlapAction' />"
398         "  </toolbar>"
400         "</ui>"
403 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
405 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
407 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
408 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
410 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
411 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
413 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
414 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
416 /* Global text entry widgets necessary for update */
417 /* GtkWidget *dropper_rgb_entry,
418           *dropper_opacity_entry ; */
419 // should be made a private member once this is converted to class
421 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
422     connection->disconnect();
423     delete connection;
426 static void purge_repr_listener( GObject* obj, GObject* tbl )
428     (void)obj;
429     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
430     if (oldrepr) { // remove old listener
431         sp_repr_remove_listener_by_data(oldrepr, tbl);
432         Inkscape::GC::release(oldrepr);
433         oldrepr = 0;
434         g_object_set_data( tbl, "repr", NULL );
435     }
438 GtkWidget *
439 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
440                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
441                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
443     SPAction *action = verb->get_action(view);
444     if (!action) return NULL;
446     SPAction *doubleclick_action;
447     if (doubleclick_verb)
448         doubleclick_action = doubleclick_verb->get_action(view);
449     else
450         doubleclick_action = NULL;
452     /* fixme: Handle sensitive/unsensitive */
453     /* fixme: Implement sp_button_new_from_action */
454     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
455     gtk_widget_show(b);
456     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
458     return b;
461 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
462                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
464     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
467 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
468                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
470     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
474 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
476     SPAction* targetAction = SP_ACTION(user_data);
477     if ( targetAction ) {
478         sp_action_perform( targetAction, NULL );
479     }
482 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
484     if ( data ) {
485         GtkAction* act = GTK_ACTION(data);
486         gtk_action_set_sensitive( act, sensitive );
487     }
490 static SPActionEventVector action_event_vector = {
491     {NULL},
492     NULL,
493     NULL,
494     sp_action_action_set_sensitive,
495     NULL,
496     NULL
497 };
499 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
501     GtkAction* act = 0;
503     SPAction* targetAction = verb->get_action(view);
504     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
505     act = GTK_ACTION(inky);
506     gtk_action_set_sensitive( act, targetAction->sensitive );
508     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
510     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
511     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
513     return act;
516 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
518     Inkscape::UI::View::View *view = desktop;
519     gint verbsToUse[] = {
520         // disabled until we have icons for them:
521         //find
522         //SP_VERB_EDIT_TILE,
523         //SP_VERB_EDIT_UNTILE,
524         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
525         SP_VERB_DIALOG_DISPLAY,
526         SP_VERB_DIALOG_FILL_STROKE,
527         SP_VERB_DIALOG_NAMEDVIEW,
528         SP_VERB_DIALOG_TEXT,
529         SP_VERB_DIALOG_XML_EDITOR,
530         SP_VERB_EDIT_CLONE,
531         SP_VERB_EDIT_COPY,
532         SP_VERB_EDIT_CUT,
533         SP_VERB_EDIT_DUPLICATE,
534         SP_VERB_EDIT_PASTE,
535         SP_VERB_EDIT_REDO,
536         SP_VERB_EDIT_UNDO,
537         SP_VERB_EDIT_UNLINK_CLONE,
538         SP_VERB_FILE_EXPORT,
539         SP_VERB_FILE_IMPORT,
540         SP_VERB_FILE_NEW,
541         SP_VERB_FILE_OPEN,
542         SP_VERB_FILE_PRINT,
543         SP_VERB_FILE_SAVE,
544         SP_VERB_OBJECT_TO_CURVE,
545         SP_VERB_SELECTION_GROUP,
546         SP_VERB_SELECTION_OUTLINE,
547         SP_VERB_SELECTION_UNGROUP,
548         SP_VERB_ZOOM_1_1,
549         SP_VERB_ZOOM_1_2,
550         SP_VERB_ZOOM_2_1,
551         SP_VERB_ZOOM_DRAWING,
552         SP_VERB_ZOOM_IN,
553         SP_VERB_ZOOM_NEXT,
554         SP_VERB_ZOOM_OUT,
555         SP_VERB_ZOOM_PAGE,
556         SP_VERB_ZOOM_PAGE_WIDTH,
557         SP_VERB_ZOOM_PREV,
558         SP_VERB_ZOOM_SELECTION,
559     };
561     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
562     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
564     static std::map<SPDesktop*, GtkActionGroup*> groups;
565     GtkActionGroup* mainActions = 0;
566     if ( groups.find(desktop) != groups.end() ) {
567         mainActions = groups[desktop];
568     }
570     if ( !mainActions ) {
571         mainActions = gtk_action_group_new("main");
572         groups[desktop] = mainActions;
573     }
575     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
576         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
577         if ( verb ) {
578             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
579                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
580                 gtk_action_group_add_action( mainActions, act );
581             }
582         }
583     }
585     return mainActions;
589 GtkWidget *
590 sp_tool_toolbox_new()
592     GtkTooltips *tt = gtk_tooltips_new();
593     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
595     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
596     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
598     gtk_widget_set_sensitive(tb, FALSE);
600     GtkWidget *hb = gtk_handle_box_new();
601     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
602     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
603     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
605     gtk_container_add(GTK_CONTAINER(hb), tb);
606     gtk_widget_show(GTK_WIDGET(tb));
608     sigc::connection* conn = new sigc::connection;
609     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
611     return hb;
614 static void
615 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
617     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
618     gtk_widget_queue_resize(child);
621 static void
622 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
624     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
625     gtk_widget_queue_resize(child);
628 GtkWidget *
629 sp_aux_toolbox_new()
631     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
633     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
634     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
635     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
636     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
637     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
639     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
640     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
642     gtk_widget_set_sensitive(tb, FALSE);
644     GtkWidget *hb = gtk_handle_box_new();
645     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
646     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
647     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
649     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
650     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
652     gtk_container_add(GTK_CONTAINER(hb), tb);
653     gtk_widget_show(GTK_WIDGET(tb));
655     sigc::connection* conn = new sigc::connection;
656     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
658     return hb;
661 //####################################
662 //# Commands Bar
663 //####################################
665 GtkWidget *
666 sp_commands_toolbox_new()
668     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
670     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
671     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
672     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
673     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
674     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
676     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
677     gtk_widget_set_sensitive(tb, FALSE);
679     GtkWidget *hb = gtk_handle_box_new();
680     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
681     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
682     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
684     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
685     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
687     gtk_container_add(GTK_CONTAINER(hb), tb);
688     gtk_widget_show(GTK_WIDGET(tb));
690     sigc::connection* conn = new sigc::connection;
691     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
693     return hb;
696 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
697                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
698                                                        gchar const *path, gchar const *data, gdouble def,
699                                                        GtkWidget *focusTarget,
700                                                        GtkWidget *us,
701                                                        GObject *dataKludge,
702                                                        gboolean altx, gchar const *altx_mark,
703                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
704                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
705                                                        void (*callback)(GtkAdjustment *, GObject *),
706                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
708     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
709                                                              lower, upper, step, page, page ) );
710     if (us) {
711         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
712     }
714     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
716     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
717     if ( shortLabel ) {
718         g_object_set( act, "short_label", shortLabel, NULL );
719     }
721     if ( (descrCount > 0) && descrLabels && descrValues ) {
722         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
723     }
725     if ( focusTarget ) {
726         ege_adjustment_action_set_focuswidget( act, focusTarget );
727     }
729     if ( altx && altx_mark ) {
730         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
731     }
733     if ( dataKludge ) {
734         g_object_set_data( dataKludge, data, adj );
735     }
737     // Using a cast just to make sure we pass in the right kind of function pointer
738     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
740     return act;
744 //####################################
745 //# node editing callbacks
746 //####################################
748 /**
749  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
750  */
751 static ShapeEditor *get_current_shape_editor()
753     if (!SP_ACTIVE_DESKTOP) {
754         return NULL;
755     }
757     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
759     if (!SP_IS_NODE_CONTEXT(event_context)) {
760         return NULL;
761     }
763     return SP_NODE_CONTEXT(event_context)->shape_editor;
767 void
768 sp_node_path_edit_add(void)
770     ShapeEditor *shape_editor = get_current_shape_editor();
771     if (shape_editor) shape_editor->add_node();
774 void
775 sp_node_path_edit_delete(void)
777     ShapeEditor *shape_editor = get_current_shape_editor();
778     if (shape_editor) shape_editor->delete_nodes();
781 void
782 sp_node_path_edit_delete_segment(void)
784     ShapeEditor *shape_editor = get_current_shape_editor();
785     if (shape_editor) shape_editor->delete_segment();
788 void
789 sp_node_path_edit_break(void)
791     ShapeEditor *shape_editor = get_current_shape_editor();
792     if (shape_editor) shape_editor->break_at_nodes();
795 void
796 sp_node_path_edit_join(void)
798     ShapeEditor *shape_editor = get_current_shape_editor();
799     if (shape_editor) shape_editor->join_nodes();
802 void
803 sp_node_path_edit_join_segment(void)
805     ShapeEditor *shape_editor = get_current_shape_editor();
806     if (shape_editor) shape_editor->join_segments();
809 void
810 sp_node_path_edit_toline(void)
812     ShapeEditor *shape_editor = get_current_shape_editor();
813     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
816 void
817 sp_node_path_edit_tocurve(void)
819     ShapeEditor *shape_editor = get_current_shape_editor();
820     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
823 void
824 sp_node_path_edit_cusp(void)
826     ShapeEditor *shape_editor = get_current_shape_editor();
827     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
830 void
831 sp_node_path_edit_smooth(void)
833     ShapeEditor *shape_editor = get_current_shape_editor();
834     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
837 void
838 sp_node_path_edit_symmetrical(void)
840     ShapeEditor *shape_editor = get_current_shape_editor();
841     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
844 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
845     bool show = gtk_toggle_action_get_active( act );
846     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
847     ShapeEditor *shape_editor = get_current_shape_editor();
848     if (shape_editor) shape_editor->show_handles(show);
851 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
852     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
855 /* is called when the node selection is modified */
856 static void
857 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
859     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
860     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
861     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
862     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
864     // quit if run by the attr_changed listener
865     if (g_object_get_data( tbl, "freeze" )) {
866         return;
867     }
869     // in turn, prevent listener from responding
870     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
872     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
873     SPUnit const *unit = tracker->getActiveUnit();
875     ShapeEditor *shape_editor = get_current_shape_editor();
876     if (shape_editor && shape_editor->has_nodepath()) {
877         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
878         int n_selected = 0;
879         if (nodepath) {
880             n_selected = nodepath->numSelected();
881         }
883         if (n_selected == 0) {
884             gtk_action_set_sensitive(xact, FALSE);
885             gtk_action_set_sensitive(yact, FALSE);
886         } else {
887             gtk_action_set_sensitive(xact, TRUE);
888             gtk_action_set_sensitive(yact, TRUE);
889             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
890             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
892             if (n_selected == 1) {
893                 NR::Point sel_node = nodepath->singleSelectedCoords();
894                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
895                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
896                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
897                 }
898             } else {
899                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
900                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
901                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
902                     /* Note: Currently x and y will always have a value, even if the coordinates of the
903                        selected nodes don't coincide (in this case we use the coordinates of the center
904                        of the bounding box). So the entries are never set to zero. */
905                     // FIXME: Maybe we should clear the entry if several nodes are selected
906                     //        instead of providing a kind of average value
907                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
908                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
909                 }
910             }
911         }
912     } else {
913         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
914         gtk_action_set_sensitive(xact, FALSE);
915         gtk_action_set_sensitive(yact, FALSE);
916     }
918     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
921 static void
922 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
924     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
926     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
927     SPUnit const *unit = tracker->getActiveUnit();
929     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
930         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
931     }
933     // quit if run by the attr_changed listener
934     if (g_object_get_data( tbl, "freeze" )) {
935         return;
936     }
938     // in turn, prevent listener from responding
939     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
941     ShapeEditor *shape_editor = get_current_shape_editor();
942     if (shape_editor && shape_editor->has_nodepath()) {
943         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
944         if (!strcmp(value_name, "x")) {
945             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
946         }
947         if (!strcmp(value_name, "y")) {
948             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
949         }
950     }
952     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
955 static void
956 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
958     sp_node_path_value_changed(adj, tbl, "x");
961 static void
962 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
964     sp_node_path_value_changed(adj, tbl, "y");
967 //################################
968 //##    Node Editing Toolbox    ##
969 //################################
971 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
973     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
974     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
975     g_object_set_data( holder, "tracker", tracker );
977     {
978         InkAction* inky = ink_action_new( "NodeInsertAction",
979                                           _("Insert node"),
980                                           _("Insert new nodes into selected segments"),
981                                           "node_insert",
982                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
983         g_object_set( inky, "short_label", _("Insert"), NULL );
984         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
985         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
986     }
988     {
989         InkAction* inky = ink_action_new( "NodeDeleteAction",
990                                           _("Delete node"),
991                                           _("Delete selected nodes"),
992                                           "node_delete",
993                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
994         g_object_set( inky, "short_label", _("Delete"), NULL );
995         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
996         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
997     }
999     {
1000         InkAction* inky = ink_action_new( "NodeJoinAction",
1001                                           _("Join endnodes"),
1002                                           _("Join selected endnodes"),
1003                                           "node_join",
1004                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1005         g_object_set( inky, "short_label", _("Join"), NULL );
1006         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1007         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1008     }
1010     {
1011         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1012                                           _("Join Segment"),
1013                                           _("Join selected endnodes with a new segment"),
1014                                           "node_join_segment",
1015                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1016         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1017         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1018     }
1020     {
1021         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1022                                           _("Delete Segment"),
1023                                           _("Split path between two non-endpoint nodes"),
1024                                           "node_delete_segment",
1025                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1026         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1027         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1028     }
1030     {
1031         InkAction* inky = ink_action_new( "NodeBreakAction",
1032                                           _("Node Break"),
1033                                           _("Break path at selected nodes"),
1034                                           "node_break",
1035                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1036         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1037         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1038     }
1040     {
1041         InkAction* inky = ink_action_new( "NodeCuspAction",
1042                                           _("Node Cusp"),
1043                                           _("Make selected nodes corner"),
1044                                           "node_cusp",
1045                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1046         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1047         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1048     }
1050     {
1051         InkAction* inky = ink_action_new( "NodeSmoothAction",
1052                                           _("Node Smooth"),
1053                                           _("Make selected nodes smooth"),
1054                                           "node_smooth",
1055                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1056         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1057         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1058     }
1060     {
1061         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1062                                           _("Node Symmetric"),
1063                                           _("Make selected nodes symmetric"),
1064                                           "node_symmetric",
1065                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1066         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1067         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1068     }
1070     {
1071         InkAction* inky = ink_action_new( "NodeLineAction",
1072                                           _("Node Line"),
1073                                           _("Make selected segments lines"),
1074                                           "node_line",
1075                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1076         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1077         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1078     }
1080     {
1081         InkAction* inky = ink_action_new( "NodeCurveAction",
1082                                           _("Node Curve"),
1083                                           _("Make selected segments curves"),
1084                                           "node_curve",
1085                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1086         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1087         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1088     }
1090     {
1091         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1092                                                       _("Show Handles"),
1093                                                       _("Show the Bezier handles of selected nodes"),
1094                                                       "nodes_show_handles",
1095                                                       Inkscape::ICON_SIZE_DECORATION );
1096         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1097         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1098         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1099     }
1101     {
1102         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1103                                           _("Next Path Effect Parameter"),
1104                                           _("Show next Path Effect parameter for editing"),
1105                                           "edit_next_parameter",
1106                                           Inkscape::ICON_SIZE_DECORATION );
1107         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1108         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1109     }
1111     /* X coord of selected node(s) */
1112     {
1113         EgeAdjustmentAction* eact = 0;
1114         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1115         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1116         eact = create_adjustment_action( "NodeXAction",
1117                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1118                                          "tools.nodes", "Xcoord", 0,
1119                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1120                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1121                                          labels, values, G_N_ELEMENTS(labels),
1122                                          sp_node_path_x_value_changed );
1123         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1124         g_object_set_data( holder, "nodes_x_action", eact );
1125         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1126         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1127     }
1129     /* Y coord of selected node(s) */
1130     {
1131         EgeAdjustmentAction* eact = 0;
1132         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1133         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1134         eact = create_adjustment_action( "NodeYAction",
1135                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1136                                          "tools.nodes", "Ycoord", 0,
1137                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1138                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1139                                          labels, values, G_N_ELEMENTS(labels),
1140                                          sp_node_path_y_value_changed );
1141         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1142         g_object_set_data( holder, "nodes_y_action", eact );
1143         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1144         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1145     }
1147     // add the units menu
1148     {
1149         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1150         gtk_action_group_add_action( mainActions, act );
1151     }
1153     sigc::connection *connection = new sigc::connection (
1154         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1155         );
1157     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1158     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1159 } // end of sp_node_toolbox_prep()
1162 //########################
1163 //##    Zoom Toolbox    ##
1164 //########################
1166 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1168     // no custom GtkAction setup needed
1169 } // end of sp_zoom_toolbox_prep()
1171 void
1172 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1174     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")));
1178 void
1179 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1181     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")));
1184 void
1185 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1187     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")));
1190 static void
1191 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1193     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1194     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1196     if (old_desktop) {
1197         GList *children, *iter;
1199         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1200         for ( iter = children ; iter ; iter = iter->next ) {
1201             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1202         }
1203         g_list_free(children);
1204     }
1206     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1208     if (desktop) {
1209         gtk_widget_set_sensitive(toolbox, TRUE);
1210         setup_func(toolbox, desktop);
1211         update_func(desktop, desktop->event_context, toolbox);
1212         *conn = desktop->connectEventContextChanged
1213             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1214     } else {
1215         gtk_widget_set_sensitive(toolbox, FALSE);
1216     }
1218 } // end of toolbox_set_desktop()
1221 static void
1222 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1224     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1225     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1226     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1227         // "toolbox.tools" was not set. Fallback to older value
1228         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1230         // Copy the setting forwards
1231         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1232     }
1233     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1235     for (int i = 0 ; tools[i].type_name ; i++ ) {
1236         GtkWidget *button =
1237             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1238                                                               SP_BUTTON_TYPE_TOGGLE,
1239                                                               Inkscape::Verb::get(tools[i].verb),
1240                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1241                                                               desktop,
1242                                                               tooltips );
1244         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1245                            (gpointer)button );
1246     }
1250 static void
1251 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1253     gchar const *const tname = ( eventcontext
1254                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1255                                  : NULL );
1256     for (int i = 0 ; tools[i].type_name ; i++ ) {
1257         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1258         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1259     }
1262 static void
1263 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1265     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1266     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1267     GtkUIManager* mgr = gtk_ui_manager_new();
1268     GError* errVal = 0;
1269     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1270     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1272     std::map<std::string, GtkWidget*> dataHolders;
1274     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1275         if ( aux_toolboxes[i].prep_func ) {
1276             // converted to GtkActions and UIManager
1278             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1279             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1280             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1281             dataHolders[aux_toolboxes[i].type_name] = kludge;
1282             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1283         } else {
1285             GtkWidget *sub_toolbox = 0;
1286             if (aux_toolboxes[i].create_func == NULL)
1287                 sub_toolbox = sp_empty_toolbox_new(desktop);
1288             else {
1289                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1290             }
1292             gtk_size_group_add_widget( grouper, sub_toolbox );
1294             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1295             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1297         }
1298     }
1300     // Second pass to create toolbars *after* all GtkActions are created
1301     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1302         if ( aux_toolboxes[i].prep_func ) {
1303             // converted to GtkActions and UIManager
1305             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1307             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1308             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1310             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1311             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1312             g_free( tmp );
1313             tmp = 0;
1315             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1316             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1317             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1318                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1319             }
1320             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1323             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1325             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1326                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1327                 swatch->setDesktop( desktop );
1328                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1329                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1330                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1331                 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 );
1332             }
1334             gtk_widget_show_all( holder );
1335             sp_set_font_size_smaller( holder );
1337             gtk_size_group_add_widget( grouper, holder );
1339             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1340             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1341         }
1342     }
1344     g_object_unref( G_OBJECT(grouper) );
1347 static void
1348 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1350     gchar const *tname = ( eventcontext
1351                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1352                            : NULL );
1353     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1354         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1355         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1356             gtk_widget_show_all(sub_toolbox);
1357             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1358         } else {
1359             gtk_widget_hide(sub_toolbox);
1360         }
1361     }
1364 static void
1365 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1367     gchar const * descr =
1368         "<ui>"
1369         "  <toolbar name='CommandsToolbar'>"
1370         "    <toolitem action='FileNew' />"
1371         "    <toolitem action='FileOpen' />"
1372         "    <toolitem action='FileSave' />"
1373         "    <toolitem action='FilePrint' />"
1374         "    <separator />"
1375         "    <toolitem action='FileImport' />"
1376         "    <toolitem action='FileExport' />"
1377         "    <separator />"
1378         "    <toolitem action='EditUndo' />"
1379         "    <toolitem action='EditRedo' />"
1380         "    <separator />"
1381         "    <toolitem action='EditCopy' />"
1382         "    <toolitem action='EditCut' />"
1383         "    <toolitem action='EditPaste' />"
1384         "    <separator />"
1385         "    <toolitem action='ZoomSelection' />"
1386         "    <toolitem action='ZoomDrawing' />"
1387         "    <toolitem action='ZoomPage' />"
1388         "    <separator />"
1389         "    <toolitem action='EditDuplicate' />"
1390         "    <toolitem action='EditClone' />"
1391         "    <toolitem action='EditUnlinkClone' />"
1392         "    <separator />"
1393         "    <toolitem action='SelectionGroup' />"
1394         "    <toolitem action='SelectionUnGroup' />"
1395         "    <separator />"
1396         "    <toolitem action='DialogFillStroke' />"
1397         "    <toolitem action='DialogText' />"
1398         "    <toolitem action='DialogXMLEditor' />"
1399         "    <toolitem action='DialogAlignDistribute' />"
1400         "    <separator />"
1401         "    <toolitem action='DialogPreferences' />"
1402         "    <toolitem action='DialogDocumentProperties' />"
1403         "  </toolbar>"
1404         "</ui>";
1405     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1408     GtkUIManager* mgr = gtk_ui_manager_new();
1409     GError* errVal = 0;
1411     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1412     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1414     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1415     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1416         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1417     }
1418     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1419     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1420     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1423     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1426 static void
1427 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1431 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1433     gtk_widget_show(toolbox_toplevel);
1434     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1436     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1437     if (!shown_toolbox) {
1438         return;
1439     }
1440     gtk_widget_show(toolbox);
1442     // need to show the spacer, or the padding will be off
1443     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1444     gtk_widget_show(spacer);
1446     gtk_widget_show_all(shown_toolbox);
1449 void
1450 aux_toolbox_space(GtkWidget *tb, gint space)
1452     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1455 static GtkWidget *
1456 sp_empty_toolbox_new(SPDesktop *desktop)
1458     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1459     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1460     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1462     gtk_widget_show_all(tbl);
1463     sp_set_font_size_smaller (tbl);
1465     return tbl;
1468 // helper UI functions
1470 GtkWidget *
1471 sp_tb_spinbutton(
1472     gchar *label, gchar const *tooltip,
1473     gchar const *path, gchar const *data, gdouble def,
1474     GtkWidget *us,
1475     GtkWidget *tbl,
1476     gboolean altx, gchar const *altx_mark,
1477     gdouble lower, gdouble upper, gdouble step, gdouble page,
1478     void (*callback)(GtkAdjustment *, GtkWidget *),
1479     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1481     GtkTooltips *tt = gtk_tooltips_new();
1483     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1485     GtkWidget *l = gtk_label_new(label);
1486     gtk_widget_show(l);
1487     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1488     gtk_container_add(GTK_CONTAINER(hb), l);
1490     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1491                                       lower, upper, step, page, page);
1492     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1493     if (us)
1494         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1496     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1497     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1498     if (altx)
1499         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1500     gtk_widget_set_size_request(sb,
1501                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1502                                 AUX_SPINBUTTON_HEIGHT);
1503     gtk_widget_show(sb);
1504     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1505     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1506     gtk_container_add(GTK_CONTAINER(hb), sb);
1507     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1509     return hb;
1512 #define MODE_LABEL_WIDTH 70
1514 //########################
1515 //##       Star         ##
1516 //########################
1518 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1520     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1522     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1523         // do not remember prefs if this call is initiated by an undo change, because undoing object
1524         // creation sets bogus values to its attributes before it is deleted
1525         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1526     }
1528     // quit if run by the attr_changed listener
1529     if (g_object_get_data( dataKludge, "freeze" )) {
1530         return;
1531     }
1533     // in turn, prevent listener from responding
1534     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1536     bool modmade = false;
1538     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1539     GSList const *items = selection->itemList();
1540     for (; items != NULL; items = items->next) {
1541         if (SP_IS_STAR((SPItem *) items->data)) {
1542             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1543             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1544             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1545                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1546                                     + M_PI / (gint)adj->value));
1547             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1548             modmade = true;
1549         }
1550     }
1551     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1552                                    _("Star: Change number of corners"));
1554     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1557 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1559     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1561     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1562         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1563     }
1565     // quit if run by the attr_changed listener
1566     if (g_object_get_data( dataKludge, "freeze" )) {
1567         return;
1568     }
1570     // in turn, prevent listener from responding
1571     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1573     bool modmade = false;
1574     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1575     GSList const *items = selection->itemList();
1576     for (; items != NULL; items = items->next) {
1577         if (SP_IS_STAR((SPItem *) items->data)) {
1578             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1580             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1581             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1582             if (r2 < r1) {
1583                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1584             } else {
1585                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1586             }
1588             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1589             modmade = true;
1590         }
1591     }
1593     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1594                                    _("Star: Change spoke ratio"));
1596     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1599 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1601     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1602     bool flat = ege_select_one_action_get_active( act ) == 0;
1604     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1605         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1606                                     flat ? "true" : "false" );
1607     }
1609     // quit if run by the attr_changed listener
1610     if (g_object_get_data( dataKludge, "freeze" )) {
1611         return;
1612     }
1614     // in turn, prevent listener from responding
1615     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1617     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1618     GSList const *items = selection->itemList();
1619     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1620     bool modmade = false;
1622     if ( prop_action ) {
1623         gtk_action_set_sensitive( prop_action, !flat );
1624     }
1626     for (; items != NULL; items = items->next) {
1627         if (SP_IS_STAR((SPItem *) items->data)) {
1628             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1629             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1630             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1631             modmade = true;
1632         }
1633     }
1635     if (modmade) {
1636         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1637                          flat ? _("Make polygon") : _("Make star"));
1638     }
1640     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1643 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1645     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1647     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1648         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1649     }
1651     // quit if run by the attr_changed listener
1652     if (g_object_get_data( dataKludge, "freeze" )) {
1653         return;
1654     }
1656     // in turn, prevent listener from responding
1657     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1659     bool modmade = false;
1661     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1662     GSList const *items = selection->itemList();
1663     for (; items != NULL; items = items->next) {
1664         if (SP_IS_STAR((SPItem *) items->data)) {
1665             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1666             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1667             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1668             modmade = true;
1669         }
1670     }
1671     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1672                                    _("Star: Change rounding"));
1674     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1677 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1679     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1681     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1682         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1683     }
1685     // quit if run by the attr_changed listener
1686     if (g_object_get_data( dataKludge, "freeze" )) {
1687         return;
1688     }
1690     // in turn, prevent listener from responding
1691     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1693     bool modmade = false;
1695     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1696     GSList const *items = selection->itemList();
1697     for (; items != NULL; items = items->next) {
1698         if (SP_IS_STAR((SPItem *) items->data)) {
1699             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1700             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1701             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1702             modmade = true;
1703         }
1704     }
1705     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1706                                    _("Star: Change randomization"));
1708     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1712 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1713                                        gchar const */*old_value*/, gchar const */*new_value*/,
1714                                        bool /*is_interactive*/, gpointer data)
1716     GtkWidget *tbl = GTK_WIDGET(data);
1718     // quit if run by the _changed callbacks
1719     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1720         return;
1721     }
1723     // in turn, prevent callbacks from responding
1724     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1726     GtkAdjustment *adj = 0;
1728     if (!strcmp(name, "inkscape:randomized")) {
1729         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1730         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1731     } else if (!strcmp(name, "inkscape:rounded")) {
1732         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1733         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1734     } else if (!strcmp(name, "inkscape:flatsided")) {
1735         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1736         char const *flatsides = repr->attribute("inkscape:flatsided");
1737         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1738         if ( flatsides && !strcmp(flatsides,"false") ) {
1739             ege_select_one_action_set_active( flat_action, 1 );
1740             gtk_action_set_sensitive( prop_action, TRUE );
1741         } else {
1742             ege_select_one_action_set_active( flat_action, 0 );
1743             gtk_action_set_sensitive( prop_action, FALSE );
1744         }
1745     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1746         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1747         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1748         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1749         if (r2 < r1) {
1750             gtk_adjustment_set_value(adj, r2/r1);
1751         } else {
1752             gtk_adjustment_set_value(adj, r1/r2);
1753         }
1754     } else if (!strcmp(name, "sodipodi:sides")) {
1755         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1756         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1757     }
1759     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1763 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1765     NULL, /* child_added */
1766     NULL, /* child_removed */
1767     star_tb_event_attr_changed,
1768     NULL, /* content_changed */
1769     NULL  /* order_changed */
1770 };
1773 /**
1774  *  \param selection Should not be NULL.
1775  */
1776 static void
1777 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1779     int n_selected = 0;
1780     Inkscape::XML::Node *repr = NULL;
1782     purge_repr_listener( tbl, tbl );
1784     for (GSList const *items = selection->itemList();
1785          items != NULL;
1786          items = items->next)
1787     {
1788         if (SP_IS_STAR((SPItem *) items->data)) {
1789             n_selected++;
1790             repr = SP_OBJECT_REPR((SPItem *) items->data);
1791         }
1792     }
1794     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1796     if (n_selected == 0) {
1797         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1798     } else if (n_selected == 1) {
1799         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1801         if (repr) {
1802             g_object_set_data( tbl, "repr", repr );
1803             Inkscape::GC::anchor(repr);
1804             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1805             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1806         }
1807     } else {
1808         // FIXME: implement averaging of all parameters for multiple selected stars
1809         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1810         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1811     }
1815 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1817     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1818     // callbacks to lump all the changes for all selected objects in one undo step
1820     GtkAdjustment *adj = 0;
1822     // fixme: make settable in prefs!
1823     gint mag = 5;
1824     gdouble prop = 0.5;
1825     gboolean flat = FALSE;
1826     gdouble randomized = 0;
1827     gdouble rounded = 0;
1829     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1830     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1832     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1833     gtk_action_set_sensitive( sb2, !flat );
1835     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1836     gtk_adjustment_set_value(adj, mag);
1837     gtk_adjustment_value_changed(adj);
1839     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1840     gtk_adjustment_set_value(adj, prop);
1841     gtk_adjustment_value_changed(adj);
1843     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1844     gtk_adjustment_set_value(adj, rounded);
1845     gtk_adjustment_value_changed(adj);
1847     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1848     gtk_adjustment_set_value(adj, randomized);
1849     gtk_adjustment_value_changed(adj);
1853 void
1854 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1856     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1857     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1858     GtkWidget *l = gtk_label_new(NULL);
1859     gtk_label_set_markup(GTK_LABEL(l), title);
1860     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1861     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1862     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1866 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1868     {
1869         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1870         ege_output_action_set_use_markup( act, TRUE );
1871         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1872         g_object_set_data( holder, "mode_action", act );
1873     }
1875     {
1876         EgeAdjustmentAction* eact = 0;
1877         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1878         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1880         /* Flatsided checkbox */
1881         {
1882             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1884             GtkTreeIter iter;
1885             gtk_list_store_append( model, &iter );
1886             gtk_list_store_set( model, &iter,
1887                                 0, _("Polygon"),
1888                                 1, _("Regular polygon (with one handle) instead of a star"),
1889                                 2, "star_flat",
1890                                 -1 );
1892             gtk_list_store_append( model, &iter );
1893             gtk_list_store_set( model, &iter,
1894                                 0, _("Star"),
1895                                 1, _("Star instead of a regular polygon (with one handle)"),
1896                                 2, "star_angled",
1897                                 -1 );
1899             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1900             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1901             g_object_set_data( holder, "flat_action", act );
1903             ege_select_one_action_set_appearance( act, "full" );
1904             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1905             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1906             ege_select_one_action_set_icon_column( act, 2 );
1907             ege_select_one_action_set_tooltip_column( act, 1  );
1909             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1910             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1911         }
1913         /* Magnitude */
1914         {
1915         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1916         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1917         eact = create_adjustment_action( "MagnitudeAction",
1918                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1919                                          "tools.shapes.star", "magnitude", 3,
1920                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1921                                          3, 1024, 1, 5,
1922                                          labels, values, G_N_ELEMENTS(labels),
1923                                          sp_stb_magnitude_value_changed,
1924                                          1.0, 0 );
1925         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1926         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1927         }
1929         /* Spoke ratio */
1930         {
1931         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1932         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1933         eact = create_adjustment_action( "SpokeAction",
1934                                          _("Spoke ratio"), _("Spoke ratio:"),
1935                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1936                                          // Base radius is the same for the closest handle.
1937                                          _("Base radius to tip radius ratio"),
1938                                          "tools.shapes.star", "proportion", 0.5,
1939                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1940                                          0.01, 1.0, 0.01, 0.1,
1941                                          labels, values, G_N_ELEMENTS(labels),
1942                                          sp_stb_proportion_value_changed );
1943         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1944         g_object_set_data( holder, "prop_action", eact );
1945         }
1947         if ( !isFlatSided ) {
1948             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1949         } else {
1950             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1951         }
1953         /* Roundedness */
1954         {
1955         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1956         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1957         eact = create_adjustment_action( "RoundednessAction",
1958                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1959                                          "tools.shapes.star", "rounded", 0.0,
1960                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1961                                          -10.0, 10.0, 0.01, 0.1,
1962                                          labels, values, G_N_ELEMENTS(labels),
1963                                          sp_stb_rounded_value_changed );
1964         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1965         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1966         }
1968         /* Randomization */
1969         {
1970         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1971         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1972         eact = create_adjustment_action( "RandomizationAction",
1973                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1974                                          "tools.shapes.star", "randomized", 0.0,
1975                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1976                                          -10.0, 10.0, 0.001, 0.01,
1977                                          labels, values, G_N_ELEMENTS(labels),
1978                                          sp_stb_randomized_value_changed, 0.1, 3 );
1979         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1980         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1981         }
1982     }
1984     {
1985         /* Reset */
1986         {
1987             GtkAction* act = gtk_action_new( "StarResetAction",
1988                                              _("Defaults"),
1989                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1990                                              GTK_STOCK_CLEAR );
1991             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1992             gtk_action_group_add_action( mainActions, act );
1993             gtk_action_set_sensitive( act, TRUE );
1994         }
1995     }
1997     sigc::connection *connection = new sigc::connection(
1998         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1999         );
2000     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2001     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2005 //########################
2006 //##       Rect         ##
2007 //########################
2009 static void sp_rtb_sensitivize( GObject *tbl )
2011     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2012     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2013     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2015     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2016         gtk_action_set_sensitive( not_rounded, FALSE );
2017     } else {
2018         gtk_action_set_sensitive( not_rounded, TRUE );
2019     }
2023 static void
2024 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2025                           void (*setter)(SPRect *, gdouble))
2027     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2029     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2030     SPUnit const *unit = tracker->getActiveUnit();
2032     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2033         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2034     }
2036     // quit if run by the attr_changed listener
2037     if (g_object_get_data( tbl, "freeze" )) {
2038         return;
2039     }
2041     // in turn, prevent listener from responding
2042     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2044     bool modmade = false;
2045     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2046     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2047         if (SP_IS_RECT(items->data)) {
2048             if (adj->value != 0) {
2049                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2050             } else {
2051                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2052             }
2053             modmade = true;
2054         }
2055     }
2057     sp_rtb_sensitivize( tbl );
2059     if (modmade) {
2060         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2061                                    _("Change rectangle"));
2062     }
2064     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2067 static void
2068 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2070     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2073 static void
2074 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2076     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2079 static void
2080 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2082     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2085 static void
2086 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2088     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2093 static void
2094 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2096     GtkAdjustment *adj = 0;
2098     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2099     gtk_adjustment_set_value(adj, 0.0);
2100     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2101     gtk_adjustment_value_changed(adj);
2103     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2104     gtk_adjustment_set_value(adj, 0.0);
2105     gtk_adjustment_value_changed(adj);
2107     sp_rtb_sensitivize( obj );
2110 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2111                                        gchar const */*old_value*/, gchar const */*new_value*/,
2112                                        bool /*is_interactive*/, gpointer data)
2114     GObject *tbl = G_OBJECT(data);
2116     // quit if run by the _changed callbacks
2117     if (g_object_get_data( tbl, "freeze" )) {
2118         return;
2119     }
2121     // in turn, prevent callbacks from responding
2122     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2124     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2125     SPUnit const *unit = tracker->getActiveUnit();
2127     gpointer item = g_object_get_data( tbl, "item" );
2128     if (item && SP_IS_RECT(item)) {
2129         {
2130             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2131             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2132             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2133         }
2135         {
2136             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2137             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2138             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2139         }
2141         {
2142             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2143             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2144             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2145         }
2147         {
2148             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2149             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2150             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2151         }
2152     }
2154     sp_rtb_sensitivize( tbl );
2156     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2160 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2161     NULL, /* child_added */
2162     NULL, /* child_removed */
2163     rect_tb_event_attr_changed,
2164     NULL, /* content_changed */
2165     NULL  /* order_changed */
2166 };
2168 /**
2169  *  \param selection should not be NULL.
2170  */
2171 static void
2172 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2174     int n_selected = 0;
2175     Inkscape::XML::Node *repr = NULL;
2176     SPItem *item = NULL;
2178     if ( g_object_get_data( tbl, "repr" ) ) {
2179         g_object_set_data( tbl, "item", NULL );
2180     }
2181     purge_repr_listener( tbl, tbl );
2183     for (GSList const *items = selection->itemList();
2184          items != NULL;
2185          items = items->next) {
2186         if (SP_IS_RECT((SPItem *) items->data)) {
2187             n_selected++;
2188             item = (SPItem *) items->data;
2189             repr = SP_OBJECT_REPR(item);
2190         }
2191     }
2193     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2195     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2197     if (n_selected == 0) {
2198         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2200         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2201         gtk_action_set_sensitive(w, FALSE);
2202         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2203         gtk_action_set_sensitive(h, FALSE);
2205     } else if (n_selected == 1) {
2206         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2207         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2209         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2210         gtk_action_set_sensitive(w, TRUE);
2211         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2212         gtk_action_set_sensitive(h, TRUE);
2214         if (repr) {
2215             g_object_set_data( tbl, "repr", repr );
2216             g_object_set_data( tbl, "item", item );
2217             Inkscape::GC::anchor(repr);
2218             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2219             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2220         }
2221     } else {
2222         // FIXME: implement averaging of all parameters for multiple selected
2223         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2224         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2225         sp_rtb_sensitivize( tbl );
2226     }
2230 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2232     EgeAdjustmentAction* eact = 0;
2234     {
2235         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2236         ege_output_action_set_use_markup( act, TRUE );
2237         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2238         g_object_set_data( holder, "mode_action", act );
2239     }
2241     // rx/ry units menu: create
2242     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2243     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2244     // fixme: add % meaning per cent of the width/height
2245     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2246     g_object_set_data( holder, "tracker", tracker );
2248     /* W */
2249     {
2250         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2251         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2252         eact = create_adjustment_action( "RectWidthAction",
2253                                          _("Width"), _("W:"), _("Width of rectangle"),
2254                                          "tools.shapes.rect", "width", 0,
2255                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2256                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2257                                          labels, values, G_N_ELEMENTS(labels),
2258                                          sp_rtb_width_value_changed );
2259         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2260         g_object_set_data( holder, "width_action", eact );
2261         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2262         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2263     }
2265     /* H */
2266     {
2267         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2268         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2269         eact = create_adjustment_action( "RectHeightAction",
2270                                          _("Height"), _("H:"), _("Height of rectangle"),
2271                                          "tools.shapes.rect", "height", 0,
2272                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2273                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2274                                          labels, values, G_N_ELEMENTS(labels),
2275                                          sp_rtb_height_value_changed );
2276         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2277         g_object_set_data( holder, "height_action", eact );
2278         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2279         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2280     }
2282     /* rx */
2283     {
2284         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2285         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2286         eact = create_adjustment_action( "RadiusXAction",
2287                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2288                                          "tools.shapes.rect", "rx", 0,
2289                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2290                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2291                                          labels, values, G_N_ELEMENTS(labels),
2292                                          sp_rtb_rx_value_changed);
2293         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2294         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2295     }
2297     /* ry */
2298     {
2299         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2300         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2301         eact = create_adjustment_action( "RadiusYAction",
2302                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2303                                          "tools.shapes.rect", "ry", 0,
2304                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2305                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2306                                          labels, values, G_N_ELEMENTS(labels),
2307                                          sp_rtb_ry_value_changed);
2308         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2309         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2310     }
2312     // add the units menu
2313     {
2314         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2315         gtk_action_group_add_action( mainActions, act );
2316     }
2318     /* Reset */
2319     {
2320         InkAction* inky = ink_action_new( "RectResetAction",
2321                                           _("Not rounded"),
2322                                           _("Make corners sharp"),
2323                                           "squared_corner",
2324                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2325         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2326         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2327         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2328         g_object_set_data( holder, "not_rounded", inky );
2329     }
2331     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2332     sp_rtb_sensitivize( holder );
2334     sigc::connection *connection = new sigc::connection(
2335         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2336         );
2337     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2338     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2341 //########################
2342 //##       3D Box       ##
2343 //########################
2345 // normalize angle so that it lies in the interval [0,360]
2346 static double box3d_normalize_angle (double a) {
2347     double angle = a + ((int) (a/360.0))*360;
2348     if (angle < 0) {
2349         angle += 360.0;
2350     }
2351     return angle;
2354 static void
2355 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2356                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2357     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2358     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2359     //       are reset).
2360     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2362     if (is_infinite) {
2363         gtk_toggle_action_set_active(tact, TRUE);
2364         gtk_action_set_sensitive(act, TRUE);
2366         double angle = persp3d_get_infinite_angle(persp, axis);
2367         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2368             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2369         }
2370     } else {
2371         gtk_toggle_action_set_active(tact, FALSE);
2372         gtk_action_set_sensitive(act, FALSE);
2373     }
2376 static void
2377 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2378     if (!persp_repr) {
2379         g_print ("No perspective given to box3d_resync_toolbar().\n");
2380         return;
2381     }
2383     GtkWidget *tbl = GTK_WIDGET(data);
2384     GtkAdjustment *adj = 0;
2385     GtkAction *act = 0;
2386     GtkToggleAction *tact = 0;
2387     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2388     {
2389         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2390         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2391         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2393         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2394     }
2395     {
2396         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2397         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2398         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2400         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2401     }
2402     {
2403         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2404         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2405         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2407         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2408     }
2411 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2412                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2413                                                   bool /*is_interactive*/, gpointer data)
2415     GtkWidget *tbl = GTK_WIDGET(data);
2417     // quit if run by the attr_changed listener
2418     // note: it used to work without the differently called freeze_ attributes (here and in
2419     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2420     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2421         return;
2422     }
2424     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2425     // sp_document_maybe_done() when the document is undo insensitive)
2426     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2428     // TODO: Only update the appropriate part of the toolbar
2429 //    if (!strcmp(name, "inkscape:vp_z")) {
2430         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2431 //    }
2433     Persp3D *persp = persp3d_get_from_repr(repr);
2434     persp3d_update_box_reprs(persp);
2436     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2439 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2441     NULL, /* child_added */
2442     NULL, /* child_removed */
2443     box3d_persp_tb_event_attr_changed,
2444     NULL, /* content_changed */
2445     NULL  /* order_changed */
2446 };
2448 /**
2449  *  \param selection Should not be NULL.
2450  */
2451 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2452 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2453 static void
2454 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2456     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2457     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2458     // update the perspectives with infinite VPs and leave the other ones untouched).
2460     Inkscape::XML::Node *persp_repr = NULL;
2461     purge_repr_listener(tbl, tbl);
2463     SPItem *item = selection->singleItem();
2464     if (item && SP_IS_BOX3D(item)) {
2465         // FIXME: Also deal with multiple selected boxes
2466         SPBox3D *box = SP_BOX3D(item);
2467         Persp3D *persp = box3d_get_perspective(box);
2468         persp_repr = SP_OBJECT_REPR(persp);
2469         if (persp_repr) {
2470             g_object_set_data(tbl, "repr", persp_repr);
2471             Inkscape::GC::anchor(persp_repr);
2472             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2473             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2474         }
2476         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2477         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2478     
2479         box3d_resync_toolbar(persp_repr, tbl);
2480     }
2483 static void
2484 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2486     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2487     SPDocument *document = sp_desktop_document(desktop);
2489     // quit if run by the attr_changed listener
2490     // note: it used to work without the differently called freeze_ attributes (here and in
2491     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2492     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2493         return;
2494     }
2496     // in turn, prevent listener from responding
2497     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2499     //Persp3D *persp = document->current_persp3d;
2500     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2501     if (sel_persps.empty()) {
2502         // this can happen when the document is created; we silently ignore it
2503         return;
2504     }
2505     Persp3D *persp = *(sel_persps.begin());
2507     persp->tmat.set_infinite_direction (axis, adj->value);
2508     SP_OBJECT(persp)->updateRepr();
2510     // TODO: use the correct axis here, too
2511     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2513     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2517 static void
2518 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2520     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2523 static void
2524 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2526     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2529 static void
2530 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2532     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2536 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2538     // TODO: Take all selected perspectives into account
2539     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2540     if (sel_persps.empty()) {
2541         // this can happen when the document is created; we silently ignore it
2542         return;
2543     }
2544     Persp3D *persp = *(sel_persps.begin());
2546     bool set_infinite = gtk_toggle_action_get_active(act);
2547     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::INFINITE : Proj::FINITE);
2550 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2552     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2555 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2557     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2560 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2562     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2565 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2567     EgeAdjustmentAction* eact = 0;
2568     SPDocument *document = sp_desktop_document (desktop);
2569     Persp3D *persp = document->current_persp3d;
2571     EgeAdjustmentAction* box3d_angle_x = 0;
2572     EgeAdjustmentAction* box3d_angle_y = 0;
2573     EgeAdjustmentAction* box3d_angle_z = 0;
2575     /* Angle X */
2576     {
2577         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2578         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2579         eact = create_adjustment_action( "3DBoxAngleXAction",
2580                                          _("Angle in X direction"), _("Angle X:"),
2581                                          // Translators: PL is short for 'perspective line'
2582                                          _("Angle of PLs in X direction"),
2583                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2584                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2585                                          -360.0, 360.0, 1.0, 10.0,
2586                                          labels, values, G_N_ELEMENTS(labels),
2587                                          box3d_angle_x_value_changed );
2588         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2589         g_object_set_data( holder, "box3d_angle_x_action", eact );
2590         box3d_angle_x = eact;
2591     }
2593     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2594         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2595     } else {
2596         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2597     }
2600     /* VP X state */
2601     {
2602         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2603                                                       // Translators: VP is short for 'vanishing point'
2604                                                       _("State of VP in X direction"),
2605                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2606                                                       "toggle_vp_x",
2607                                                       Inkscape::ICON_SIZE_DECORATION );
2608         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2609         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2610         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2611         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2612         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2613     }
2615     /* Angle Y */
2616     {
2617         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2618         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2619         eact = create_adjustment_action( "3DBoxAngleYAction",
2620                                          _("Angle in Y direction"), _("Angle Y:"),
2621                                          // Translators: PL is short for 'perspective line'
2622                                          _("Angle of PLs in Y direction"),
2623                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2624                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2625                                          -360.0, 360.0, 1.0, 10.0,
2626                                          labels, values, G_N_ELEMENTS(labels),
2627                                          box3d_angle_y_value_changed );
2628         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2629         g_object_set_data( holder, "box3d_angle_y_action", eact );
2630         box3d_angle_y = eact;
2631     }
2633     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2634         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2635     } else {
2636         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2637     }
2639     /* VP Y state */
2640     {
2641         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2642                                                       // Translators: VP is short for 'vanishing point'
2643                                                       _("State of VP in Y direction"),
2644                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2645                                                       "toggle_vp_y",
2646                                                       Inkscape::ICON_SIZE_DECORATION );
2647         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2648         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2649         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2650         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2651         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2652     }
2654     /* Angle Z */
2655     {
2656         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2657         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2658         eact = create_adjustment_action( "3DBoxAngleZAction",
2659                                          _("Angle in Z direction"), _("Angle Z:"),
2660                                          // Translators: PL is short for 'perspective line'
2661                                          _("Angle of PLs in Z direction"),
2662                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2663                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2664                                          -360.0, 360.0, 1.0, 10.0,
2665                                          labels, values, G_N_ELEMENTS(labels),
2666                                          box3d_angle_z_value_changed );
2667         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2668         g_object_set_data( holder, "box3d_angle_z_action", eact );
2669         box3d_angle_z = eact;
2670     }
2672     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2673         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2674     } else {
2675         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2676     }
2678     /* VP Z state */
2679     {
2680         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2681                                                       // Translators: VP is short for 'vanishing point'
2682                                                       _("State of VP in Z direction"),
2683                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2684                                                       "toggle_vp_z",
2685                                                       Inkscape::ICON_SIZE_DECORATION );
2686         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2687         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2688         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2689         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2690         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2691     }
2693     sigc::connection *connection = new sigc::connection(
2694         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2695        );
2696     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2697     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2700 //########################
2701 //##       Spiral       ##
2702 //########################
2704 static void
2705 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2707     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2709     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2710         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2711     }
2713     // quit if run by the attr_changed listener
2714     if (g_object_get_data( tbl, "freeze" )) {
2715         return;
2716     }
2718     // in turn, prevent listener from responding
2719     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2721     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2723     bool modmade = false;
2724     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2725          items != NULL;
2726          items = items->next)
2727     {
2728         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2729             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2730             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2731             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2732             modmade = true;
2733         }
2734     }
2736     g_free(namespaced_name);
2738     if (modmade) {
2739         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2740                                    _("Change spiral"));
2741     }
2743     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2746 static void
2747 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2749     sp_spl_tb_value_changed(adj, tbl, "revolution");
2752 static void
2753 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2755     sp_spl_tb_value_changed(adj, tbl, "expansion");
2758 static void
2759 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2761     sp_spl_tb_value_changed(adj, tbl, "t0");
2764 static void
2765 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2767     GtkWidget *tbl = GTK_WIDGET(obj);
2769     GtkAdjustment *adj;
2771     // fixme: make settable
2772     gdouble rev = 5;
2773     gdouble exp = 1.0;
2774     gdouble t0 = 0.0;
2776     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2777     gtk_adjustment_set_value(adj, rev);
2778     gtk_adjustment_value_changed(adj);
2780     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2781     gtk_adjustment_set_value(adj, exp);
2782     gtk_adjustment_value_changed(adj);
2784     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2785     gtk_adjustment_set_value(adj, t0);
2786     gtk_adjustment_value_changed(adj);
2788     spinbutton_defocus(GTK_OBJECT(tbl));
2792 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2793                                          gchar const */*old_value*/, gchar const */*new_value*/,
2794                                          bool /*is_interactive*/, gpointer data)
2796     GtkWidget *tbl = GTK_WIDGET(data);
2798     // quit if run by the _changed callbacks
2799     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2800         return;
2801     }
2803     // in turn, prevent callbacks from responding
2804     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2806     GtkAdjustment *adj;
2807     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2808     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2810     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2811     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2813     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2814     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2816     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2820 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2821     NULL, /* child_added */
2822     NULL, /* child_removed */
2823     spiral_tb_event_attr_changed,
2824     NULL, /* content_changed */
2825     NULL  /* order_changed */
2826 };
2828 static void
2829 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2831     int n_selected = 0;
2832     Inkscape::XML::Node *repr = NULL;
2834     purge_repr_listener( tbl, tbl );
2836     for (GSList const *items = selection->itemList();
2837          items != NULL;
2838          items = items->next)
2839     {
2840         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2841             n_selected++;
2842             repr = SP_OBJECT_REPR((SPItem *) items->data);
2843         }
2844     }
2846     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2848     if (n_selected == 0) {
2849         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2850     } else if (n_selected == 1) {
2851         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2853         if (repr) {
2854             g_object_set_data( tbl, "repr", repr );
2855             Inkscape::GC::anchor(repr);
2856             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2857             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2858         }
2859     } else {
2860         // FIXME: implement averaging of all parameters for multiple selected
2861         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2862         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2863     }
2867 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2869     EgeAdjustmentAction* eact = 0;
2871     {
2872         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2873         ege_output_action_set_use_markup( act, TRUE );
2874         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2875         g_object_set_data( holder, "mode_action", act );
2876     }
2878     /* Revolution */
2879     {
2880         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2881         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2882         eact = create_adjustment_action( "SpiralRevolutionAction",
2883                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2884                                          "tools.shapes.spiral", "revolution", 3.0,
2885                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2886                                          0.01, 1024.0, 0.1, 1.0,
2887                                          labels, values, G_N_ELEMENTS(labels),
2888                                          sp_spl_tb_revolution_value_changed, 1, 2);
2889         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2890     }
2892     /* Expansion */
2893     {
2894         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2895         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2896         eact = create_adjustment_action( "SpiralExpansionAction",
2897                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2898                                          "tools.shapes.spiral", "expansion", 1.0,
2899                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2900                                          0.0, 1000.0, 0.01, 1.0,
2901                                          labels, values, G_N_ELEMENTS(labels),
2902                                          sp_spl_tb_expansion_value_changed);
2903         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2904     }
2906     /* T0 */
2907     {
2908         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2909         gdouble values[] = {0, 0.5, 0.9};
2910         eact = create_adjustment_action( "SpiralT0Action",
2911                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2912                                          "tools.shapes.spiral", "t0", 0.0,
2913                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2914                                          0.0, 0.999, 0.01, 1.0,
2915                                          labels, values, G_N_ELEMENTS(labels),
2916                                          sp_spl_tb_t0_value_changed);
2917         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2918     }
2920     /* Reset */
2921     {
2922         InkAction* inky = ink_action_new( "SpiralResetAction",
2923                                           _("Defaults"),
2924                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2925                                           GTK_STOCK_CLEAR,
2926                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2927         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2928         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2929     }
2932     sigc::connection *connection = new sigc::connection(
2933         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2934         );
2935     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2936     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2939 //########################
2940 //##     Pen/Pencil    ##
2941 //########################
2944 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2946     // Put stuff here
2949 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2951     // Put stuff here
2954 //########################
2955 //##       Tweak        ##
2956 //########################
2958 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2960     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2963 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2965     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2968 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2970     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2973 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2975     int mode = ege_select_one_action_get_active( act );
2976     prefs_set_int_attribute("tools.tweak", "mode", mode);
2978     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2979     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2980     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2981     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2982     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2983     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2984     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2985         if (doh) gtk_action_set_sensitive (doh, TRUE);
2986         if (dos) gtk_action_set_sensitive (dos, TRUE);
2987         if (dol) gtk_action_set_sensitive (dol, TRUE);
2988         if (doo) gtk_action_set_sensitive (doo, TRUE);
2989         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2990         if (fid) gtk_action_set_sensitive (fid, FALSE);
2991     } else {
2992         if (doh) gtk_action_set_sensitive (doh, FALSE);
2993         if (dos) gtk_action_set_sensitive (dos, FALSE);
2994         if (dol) gtk_action_set_sensitive (dol, FALSE);
2995         if (doo) gtk_action_set_sensitive (doo, FALSE);
2996         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2997         if (fid) gtk_action_set_sensitive (fid, TRUE);
2998     }
3001 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3003     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3006 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3007     bool show = gtk_toggle_action_get_active( act );
3008     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3010 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3011     bool show = gtk_toggle_action_get_active( act );
3012     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3014 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3015     bool show = gtk_toggle_action_get_active( act );
3016     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3018 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3019     bool show = gtk_toggle_action_get_active( act );
3020     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3023 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3025     {
3026         /* Width */
3027         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3028         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3029         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3030                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3031                                                               "tools.tweak", "width", 15,
3032                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3033                                                               1, 100, 1.0, 10.0,
3034                                                               labels, values, G_N_ELEMENTS(labels),
3035                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3036         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3037         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3038     }
3041     {
3042         /* Force */
3043         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3044         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3045         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3046                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3047                                                               "tools.tweak", "force", 20,
3048                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3049                                                               1, 100, 1.0, 10.0,
3050                                                               labels, values, G_N_ELEMENTS(labels),
3051                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3052         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3053         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3054     }
3056     /* Mode */
3057     {
3058         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3060         GtkTreeIter iter;
3061         gtk_list_store_append( model, &iter );
3062         gtk_list_store_set( model, &iter,
3063                             0, _("Push mode"),
3064                             1, _("Push parts of paths in any direction"),
3065                             2, "tweak_push_mode",
3066                             -1 );
3068         gtk_list_store_append( model, &iter );
3069         gtk_list_store_set( model, &iter,
3070                             0, _("Shrink mode"),
3071                             1, _("Shrink (inset) parts of paths"),
3072                             2, "tweak_shrink_mode",
3073                             -1 );
3075         gtk_list_store_append( model, &iter );
3076         gtk_list_store_set( model, &iter,
3077                             0, _("Grow mode"),
3078                             1, _("Grow (outset) parts of paths"),
3079                             2, "tweak_grow_mode",
3080                             -1 );
3082         gtk_list_store_append( model, &iter );
3083         gtk_list_store_set( model, &iter,
3084                             0, _("Attract mode"),
3085                             1, _("Attract parts of paths towards cursor"),
3086                             2, "tweak_attract_mode",
3087                             -1 );
3089         gtk_list_store_append( model, &iter );
3090         gtk_list_store_set( model, &iter,
3091                             0, _("Repel mode"),
3092                             1, _("Repel parts of paths from cursor"),
3093                             2, "tweak_repel_mode",
3094                             -1 );
3096         gtk_list_store_append( model, &iter );
3097         gtk_list_store_set( model, &iter,
3098                             0, _("Roughen mode"),
3099                             1, _("Roughen parts of paths"),
3100                             2, "tweak_roughen_mode",
3101                             -1 );
3103         gtk_list_store_append( model, &iter );
3104         gtk_list_store_set( model, &iter,
3105                             0, _("Color paint mode"),
3106                             1, _("Paint the tool's color upon selected objects"),
3107                             2, "tweak_colorpaint_mode",
3108                             -1 );
3110         gtk_list_store_append( model, &iter );
3111         gtk_list_store_set( model, &iter,
3112                             0, _("Color jitter mode"),
3113                             1, _("Jitter the colors of selected objects"),
3114                             2, "tweak_colorjitter_mode",
3115                             -1 );
3117         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3118         g_object_set( act, "short_label", _("Mode:"), NULL );
3119         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3120         g_object_set_data( holder, "mode_action", act );
3122         ege_select_one_action_set_appearance( act, "full" );
3123         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3124         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3125         ege_select_one_action_set_icon_column( act, 2 );
3126         ege_select_one_action_set_tooltip_column( act, 1  );
3128         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3129         ege_select_one_action_set_active( act, mode );
3130         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3132         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3133     }
3135     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3137     {
3138         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3139         ege_output_action_set_use_markup( act, TRUE );
3140         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3141         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3142             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3143         g_object_set_data( holder, "tweak_channels_label", act);
3144     }
3146     {
3147         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3148                                                       _("Hue"),
3149                                                       _("In color mode, act on objects' hue"),
3150                                                       NULL,
3151                                                       Inkscape::ICON_SIZE_DECORATION );
3152         g_object_set( act, "short_label", _("H"), NULL );
3153         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3154         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3155         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3156         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3157             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3158         g_object_set_data( holder, "tweak_doh", act);
3159     }
3160     {
3161         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3162                                                       _("Saturation"),
3163                                                       _("In color mode, act on objects' saturation"),
3164                                                       NULL,
3165                                                       Inkscape::ICON_SIZE_DECORATION );
3166         g_object_set( act, "short_label", _("S"), NULL );
3167         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3168         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3169         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3170         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3171             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3172         g_object_set_data( holder, "tweak_dos", act );
3173     }
3174     {
3175         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3176                                                       _("Lightness"),
3177                                                       _("In color mode, act on objects' lightness"),
3178                                                       NULL,
3179                                                       Inkscape::ICON_SIZE_DECORATION );
3180         g_object_set( act, "short_label", _("L"), NULL );
3181         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3182         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3183         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3184         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3185             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3186         g_object_set_data( holder, "tweak_dol", act );
3187     }
3188     {
3189         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3190                                                       _("Opacity"),
3191                                                       _("In color mode, act on objects' opacity"),
3192                                                       NULL,
3193                                                       Inkscape::ICON_SIZE_DECORATION );
3194         g_object_set( act, "short_label", _("O"), NULL );
3195         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3196         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3197         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3198         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3199             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3200         g_object_set_data( holder, "tweak_doo", act );
3201     }
3203     {   /* Fidelity */
3204         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3205         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3206         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3207                                                               _("Fidelity"), _("Fidelity:"),
3208                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3209                                                               "tools.tweak", "fidelity", 50,
3210                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3211                                                               1, 100, 1.0, 10.0,
3212                                                               labels, values, G_N_ELEMENTS(labels),
3213                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3214         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3215         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3216         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3217             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3218         g_object_set_data( holder, "tweak_fidelity", eact );
3219     }
3222     /* Use Pressure button */
3223     {
3224         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3225                                                       _("Pressure"),
3226                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3227                                                       "use_pressure",
3228                                                       Inkscape::ICON_SIZE_DECORATION );
3229         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3230         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3231         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3232     }
3237 //########################
3238 //##     Calligraphy    ##
3239 //########################
3241 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3243     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3246 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3248     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3251 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3253     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3256 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3258     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3261 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3263     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3266 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3268     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3271 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3273     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3276 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3278     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3281 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3283     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3286 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3288     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3291 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3293     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3295     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3298 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3300     // FIXME: make defaults settable via Inkscape Options
3301     struct KeyValue {
3302         char const *key;
3303         double value;
3304     } const key_values[] = {
3305         {"mass", 0.02},
3306         {"wiggle", 0.0},
3307         {"angle", 30.0},
3308         {"width", 15},
3309         {"thinning", 0.1},
3310         {"tremor", 0.0},
3311         {"flatness", 0.9},
3312         {"cap_rounding", 0.0}
3313     };
3315     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3316         KeyValue const &kv = key_values[i];
3317         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3318         if ( adj ) {
3319             gtk_adjustment_set_value(adj, kv.value);
3320         }
3321     }
3325 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3327     {
3328         EgeAdjustmentAction* calligraphy_angle = 0;
3330         {
3331         /* Width */
3332         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3333         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3334         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3335                                                               _("Pen Width"), _("Width:"),
3336                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3337                                                               "tools.calligraphic", "width", 15,
3338                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3339                                                               1, 100, 1.0, 10.0,
3340                                                               labels, values, G_N_ELEMENTS(labels),
3341                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3342         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3343         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3344         }
3346         {
3347         /* Thinning */
3348             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3349             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3350         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3351                                                               _("Stroke Thinning"), _("Thinning:"),
3352                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3353                                                               "tools.calligraphic", "thinning", 0.1,
3354                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3355                                                               -1.0, 1.0, 0.01, 0.1,
3356                                                               labels, values, G_N_ELEMENTS(labels),
3357                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3358         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3359         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3360         }
3362         {
3363         /* Angle */
3364         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3365         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3366         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3367                                                               _("Pen Angle"), _("Angle:"),
3368                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3369                                                               "tools.calligraphic", "angle", 30,
3370                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3371                                                               -90.0, 90.0, 1.0, 10.0,
3372                                                               labels, values, G_N_ELEMENTS(labels),
3373                                                               sp_ddc_angle_value_changed, 1, 0 );
3374         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3375         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3376         calligraphy_angle = eact;
3377         }
3379         {
3380         /* Fixation */
3381             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3382         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3383         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3384                                                               _("Fixation"), _("Fixation:"),
3385                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3386                                                               "tools.calligraphic", "flatness", 0.9,
3387                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3388                                                               0.0, 1.0, 0.01, 0.1,
3389                                                               labels, values, G_N_ELEMENTS(labels),
3390                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3391         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3392         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3393         }
3395         {
3396         /* Cap Rounding */
3397             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3398         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3399         // TRANSLATORS: "cap" means "end" (both start and finish) here
3400         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3401                                                               _("Cap rounding"), _("Caps:"),
3402                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3403                                                               "tools.calligraphic", "cap_rounding", 0.0,
3404                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3405                                                               0.0, 5.0, 0.01, 0.1,
3406                                                               labels, values, G_N_ELEMENTS(labels),
3407                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3408         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3409         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3410         }
3412         {
3413         /* Tremor */
3414             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3415         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3416         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3417                                                               _("Stroke Tremor"), _("Tremor:"),
3418                                                               _("Increase to make strokes rugged and trembling"),
3419                                                               "tools.calligraphic", "tremor", 0.0,
3420                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3421                                                               0.0, 1.0, 0.01, 0.1,
3422                                                               labels, values, G_N_ELEMENTS(labels),
3423                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3425         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3426         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3427         }
3429         {
3430         /* Wiggle */
3431         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3432         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3433         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3434                                                               _("Pen Wiggle"), _("Wiggle:"),
3435                                                               _("Increase to make the pen waver and wiggle"),
3436                                                               "tools.calligraphic", "wiggle", 0.0,
3437                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3438                                                               0.0, 1.0, 0.01, 0.1,
3439                                                               labels, values, G_N_ELEMENTS(labels),
3440                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3441         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3442         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3443         }
3445         {
3446         /* Mass */
3447             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3448         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3449         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3450                                                               _("Pen Mass"), _("Mass:"),
3451                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3452                                                               "tools.calligraphic", "mass", 0.02,
3453                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3454                                                               0.0, 1.0, 0.01, 0.1,
3455                                                               labels, values, G_N_ELEMENTS(labels),
3456                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3457         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3458         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3459         }
3462         /* Trace Background button */
3463         {
3464             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3465                                                           _("Trace Background"),
3466                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3467                                                           "trace_background",
3468                                                           Inkscape::ICON_SIZE_DECORATION );
3469             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3470             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3471             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3472         }
3474         /* Use Pressure button */
3475         {
3476             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3477                                                           _("Pressure"),
3478                                                           _("Use the pressure of the input device to alter the width of the pen"),
3479                                                           "use_pressure",
3480                                                           Inkscape::ICON_SIZE_DECORATION );
3481             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3482             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3483             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3484         }
3486         /* Use Tilt button */
3487         {
3488             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3489                                                           _("Tilt"),
3490                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3491                                                           "use_tilt",
3492                                                           Inkscape::ICON_SIZE_DECORATION );
3493             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3494             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3495             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3496             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3497         }
3499         /* Reset */
3500         {
3501             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3502                                              _("Defaults"),
3503                                              _("Reset all parameters to defaults"),
3504                                              GTK_STOCK_CLEAR );
3505             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3506             gtk_action_group_add_action( mainActions, act );
3507             gtk_action_set_sensitive( act, TRUE );
3508         }
3509     }
3513 //########################
3514 //##    Circle / Arc    ##
3515 //########################
3517 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3519     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3520     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3522     if (v1 == 0 && v2 == 0) {
3523         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3524             gtk_action_set_sensitive( ocb, FALSE );
3525             gtk_action_set_sensitive( make_whole, FALSE );
3526         }
3527     } else {
3528         gtk_action_set_sensitive( ocb, TRUE );
3529         gtk_action_set_sensitive( make_whole, TRUE );
3530     }
3533 static void
3534 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3536     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3538     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3539         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3540     }
3542     // quit if run by the attr_changed listener
3543     if (g_object_get_data( tbl, "freeze" )) {
3544         return;
3545     }
3547     // in turn, prevent listener from responding
3548     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3550     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3552     bool modmade = false;
3553     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3554          items != NULL;
3555          items = items->next)
3556     {
3557         SPItem *item = SP_ITEM(items->data);
3559         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3561             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3562             SPArc *arc = SP_ARC(item);
3564             if (!strcmp(value_name, "start"))
3565                 ge->start = (adj->value * M_PI)/ 180;
3566             else
3567                 ge->end = (adj->value * M_PI)/ 180;
3569             sp_genericellipse_normalize(ge);
3570             ((SPObject *)arc)->updateRepr();
3571             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3573             modmade = true;
3574         }
3575     }
3577     g_free(namespaced_name);
3579     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3581     sp_arctb_sensitivize( tbl, adj->value, other->value );
3583     if (modmade) {
3584         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3585                                    _("Arc: Change start/end"));
3586     }
3588     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3592 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3594     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3597 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3599     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3602 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3604     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3605     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3606         if ( ege_select_one_action_get_active( act ) != 0 ) {
3607             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3608         } else {
3609             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3610         }
3611     }
3613     // quit if run by the attr_changed listener
3614     if (g_object_get_data( tbl, "freeze" )) {
3615         return;
3616     }
3618     // in turn, prevent listener from responding
3619     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3621     bool modmade = false;
3623     if ( ege_select_one_action_get_active(act) != 0 ) {
3624         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3625              items != NULL;
3626              items = items->next)
3627         {
3628             if (SP_IS_ARC((SPItem *) items->data)) {
3629                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3630                 repr->setAttribute("sodipodi:open", "true");
3631                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3632                 modmade = true;
3633             }
3634         }
3635     } else {
3636         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3637              items != NULL;
3638              items = items->next)
3639         {
3640             if (SP_IS_ARC((SPItem *) items->data))    {
3641                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3642                 repr->setAttribute("sodipodi:open", NULL);
3643                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3644                 modmade = true;
3645             }
3646         }
3647     }
3649     if (modmade) {
3650         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3651                                    _("Arc: Change open/closed"));
3652     }
3654     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3657 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3659     GtkAdjustment *adj;
3660     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3661     gtk_adjustment_set_value(adj, 0.0);
3662     gtk_adjustment_value_changed(adj);
3664     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3665     gtk_adjustment_set_value(adj, 0.0);
3666     gtk_adjustment_value_changed(adj);
3668     spinbutton_defocus( GTK_OBJECT(obj) );
3671 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3672                                       gchar const */*old_value*/, gchar const */*new_value*/,
3673                                       bool /*is_interactive*/, gpointer data)
3675     GObject *tbl = G_OBJECT(data);
3677     // quit if run by the _changed callbacks
3678     if (g_object_get_data( tbl, "freeze" )) {
3679         return;
3680     }
3682     // in turn, prevent callbacks from responding
3683     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3685     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3686     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3688     GtkAdjustment *adj1,*adj2;
3689     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3690     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3691     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3692     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3694     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3696     char const *openstr = NULL;
3697     openstr = repr->attribute("sodipodi:open");
3698     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3700     if (openstr) {
3701         ege_select_one_action_set_active( ocb, 1 );
3702     } else {
3703         ege_select_one_action_set_active( ocb, 0 );
3704     }
3706     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3709 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3710     NULL, /* child_added */
3711     NULL, /* child_removed */
3712     arc_tb_event_attr_changed,
3713     NULL, /* content_changed */
3714     NULL  /* order_changed */
3715 };
3718 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3720     int n_selected = 0;
3721     Inkscape::XML::Node *repr = NULL;
3723     purge_repr_listener( tbl, tbl );
3725     for (GSList const *items = selection->itemList();
3726          items != NULL;
3727          items = items->next)
3728     {
3729         if (SP_IS_ARC((SPItem *) items->data)) {
3730             n_selected++;
3731             repr = SP_OBJECT_REPR((SPItem *) items->data);
3732         }
3733     }
3735     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3737     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3738     if (n_selected == 0) {
3739         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3740     } else if (n_selected == 1) {
3741         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3742         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3744         if (repr) {
3745             g_object_set_data( tbl, "repr", repr );
3746             Inkscape::GC::anchor(repr);
3747             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3748             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3749         }
3750     } else {
3751         // FIXME: implement averaging of all parameters for multiple selected
3752         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3753         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3754         sp_arctb_sensitivize( tbl, 1, 0 );
3755     }
3759 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3761     EgeAdjustmentAction* eact = 0;
3764     {
3765         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3766         ege_output_action_set_use_markup( act, TRUE );
3767         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3768         g_object_set_data( holder, "mode_action", act );
3769     }
3771     /* Start */
3772     {
3773         eact = create_adjustment_action( "ArcStartAction",
3774                                          _("Start"), _("Start:"),
3775                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3776                                          "tools.shapes.arc", "start", 0.0,
3777                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3778                                          -360.0, 360.0, 1.0, 10.0,
3779                                          0, 0, 0,
3780                                          sp_arctb_start_value_changed);
3781         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3782     }
3784     /* End */
3785     {
3786         eact = create_adjustment_action( "ArcEndAction",
3787                                          _("End"), _("End:"),
3788                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3789                                          "tools.shapes.arc", "end", 0.0,
3790                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3791                                          -360.0, 360.0, 1.0, 10.0,
3792                                          0, 0, 0,
3793                                          sp_arctb_end_value_changed);
3794         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3795     }
3797     /* Segments / Pie checkbox */
3798     {
3799         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3801         GtkTreeIter iter;
3802         gtk_list_store_append( model, &iter );
3803         gtk_list_store_set( model, &iter,
3804                             0, _("Closed arc"),
3805                             1, _("Switch to segment (closed shape with two radii)"),
3806                             2, "circle_closed_arc",
3807                             -1 );
3809         gtk_list_store_append( model, &iter );
3810         gtk_list_store_set( model, &iter,
3811                             0, _("Open Arc"),
3812                             1, _("Switch to arc (unclosed shape)"),
3813                             2, "circle_open_arc",
3814                             -1 );
3816         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3817         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3818         g_object_set_data( holder, "open_action", act );
3820         ege_select_one_action_set_appearance( act, "full" );
3821         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3822         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3823         ege_select_one_action_set_icon_column( act, 2 );
3824         ege_select_one_action_set_tooltip_column( act, 1  );
3826         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3827         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3828         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3829         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3830     }
3832     /* Make Whole */
3833     {
3834         InkAction* inky = ink_action_new( "ArcResetAction",
3835                                           _("Make whole"),
3836                                           _("Make the shape a whole ellipse, not arc or segment"),
3837                                           "reset_circle",
3838                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3839         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3840         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3841         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3842         g_object_set_data( holder, "make_whole", inky );
3843     }
3845     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3846     // sensitivize make whole and open checkbox
3847     {
3848         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3849         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3850         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3851     }
3854     sigc::connection *connection = new sigc::connection(
3855         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3856         );
3857     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3858     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3864 // toggle button callbacks and updaters
3866 //########################
3867 //##      Dropper       ##
3868 //########################
3870 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3871     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3872     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3873     if ( set_action ) {
3874         if ( gtk_toggle_action_get_active( act ) ) {
3875             gtk_action_set_sensitive( set_action, TRUE );
3876         } else {
3877             gtk_action_set_sensitive( set_action, FALSE );
3878         }
3879     }
3881     spinbutton_defocus(GTK_OBJECT(tbl));
3884 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3885     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3886     spinbutton_defocus(GTK_OBJECT(tbl));
3890 /**
3891  * Dropper auxiliary toolbar construction and setup.
3892  *
3893  * TODO: Would like to add swatch of current color.
3894  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3895  *       can drag and drop places. Will provide a nice mixing palette.
3896  */
3897 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3899     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3901     {
3902         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3903                                                       _("Pick alpha"),
3904                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3905                                                       "color_alpha_get",
3906                                                       Inkscape::ICON_SIZE_DECORATION );
3907         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3908         g_object_set_data( holder, "pick_action", act );
3909         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3910         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3911     }
3913     {
3914         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3915                                                       _("Set alpha"),
3916                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3917                                                       "color_alpha_set",
3918                                                       Inkscape::ICON_SIZE_DECORATION );
3919         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3920         g_object_set_data( holder, "set_action", act );
3921         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3922         // make sure it's disabled if we're not picking alpha
3923         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3924         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3925     }
3929 //########################
3930 //##    Text Toolbox    ##
3931 //########################
3932 /*
3933 static void
3934 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3936     //Call back for letter sizing spinbutton
3939 static void
3940 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3942     //Call back for line height spinbutton
3945 static void
3946 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3948     //Call back for horizontal kerning spinbutton
3951 static void
3952 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3954     //Call back for vertical kerning spinbutton
3957 static void
3958 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3960     //Call back for letter rotation spinbutton
3961 }*/
3963 namespace {
3965 bool visible = false;
3967 void
3968 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3970     SPStyle *query =
3971         sp_style_new (SP_ACTIVE_DOCUMENT);
3972     
3973     int result_fontspec =
3974         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3976     int result_family =
3977         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3979     int result_style =
3980         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3982     int result_numbers =
3983         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3985     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3987     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3988     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3989     {
3990         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3992         if (repr)
3993         {
3994             sp_style_read_from_repr (query, repr);
3995         }
3996         else
3997         {
3998             return;
3999         }
4000     }
4002     if (query->text)
4003     {
4004         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4005             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4006             gtk_entry_set_text (GTK_ENTRY (entry), "");
4008         } else if (query->text->font_specification.value || query->text->font_family.value) {
4010             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4011             
4012             // Get the font that corresponds 
4013             Glib::ustring familyName;
4014             
4015             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4016             if (font) {
4017                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4018                 font->Unref();
4019                 font = NULL;
4020             }
4021             
4022             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4024             Gtk::TreePath path;
4025             try {
4026                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4027             } catch (...) {
4028                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4029                 return;
4030             }
4032             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4033             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4035             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4037             gtk_tree_selection_select_path (tselection, path.gobj());
4038             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4040             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4041         }
4043         //Size
4044         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4045         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4046         g_object_set_data (tbl, "size-block", gpointer(1));
4047         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4048         g_object_set_data (tbl, "size-block", gpointer(0));
4049         free (str);
4051         //Anchor
4052         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4053         {
4054             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4055             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4056             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4057             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4058         }
4059         else
4060         {
4061             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4062             {
4063                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4064                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4065                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4066                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4067             }
4068             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4069             {
4070                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4071                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4072                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4073                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4074             }
4075             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4076             {
4077                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4078                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4079                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4080                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4081             }
4082         }
4084         //Style
4085         {
4086             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4088             gboolean active = gtk_toggle_button_get_active (button);
4089             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4091             if (active != check)
4092             {
4093                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4094                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4095                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4096             }
4097         }
4099         {
4100             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4102             gboolean active = gtk_toggle_button_get_active (button);
4103             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4105             if (active != check)
4106             {
4107                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4108                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4109                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4110             }
4111         }
4113         //Orientation
4114         //locking both buttons, changing one affect all group (both)
4115         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4116         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4118         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4119         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4121         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4122         {
4123             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4124         }
4125         else
4126         {
4127             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4128         }
4129         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4130         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4131     }
4133     sp_style_unref(query);
4136 void
4137 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4139     sp_text_toolbox_selection_changed (selection, tbl);
4142 void
4143 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4145     sp_text_toolbox_selection_changed (NULL, tbl);
4148 void
4149 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4150                                 GObject             *tbl)
4152     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4153     GtkTreeModel *model = 0;
4154     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4155     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4156     GtkTreeIter   iter;
4157     char         *family = 0;
4159     (void)popdown;
4161     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4162     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4164     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4165         return;
4166     }
4168     gtk_tree_model_get (model, &iter, 0, &family, -1);
4170     if (g_object_get_data (G_OBJECT (selection), "block"))
4171     {
4172         gtk_entry_set_text (GTK_ENTRY (entry), family);
4173         return;
4174     }
4176     gtk_entry_set_text (GTK_ENTRY (entry), family);
4178     SPStyle *query =
4179         sp_style_new (SP_ACTIVE_DOCUMENT);
4181     int result_fontspec =
4182         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4183     
4184     SPCSSAttr *css = sp_repr_css_attr_new ();
4185     
4186     std::string fontSpec = query->text->font_specification.value;
4187     if (!fontSpec.empty()) {
4188         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4189         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4190             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4191             if (font) {
4192                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4193                 
4194                 // Set all the these just in case they were altered when finding the best
4195                 // match for the new family and old style...
4196                 
4197                 gchar c[256];
4198                 
4199                 font->Family(c, 256);
4200                 sp_repr_css_set_property (css, "font-family", c);
4201                 
4202                 font->Attribute( "weight", c, 256);
4203                 sp_repr_css_set_property (css, "font-weight", c);
4204                 
4205                 font->Attribute("style", c, 256);
4206                 sp_repr_css_set_property (css, "font-style", c);
4207                 
4208                 font->Attribute("stretch", c, 256);
4209                 sp_repr_css_set_property (css, "font-stretch", c);
4210                 
4211                 font->Attribute("variant", c, 256);
4212                 sp_repr_css_set_property (css, "font-variant", c);
4213                 
4214                 font->Unref();
4215             }
4216         }
4217     }
4219     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4220     if (result_fontspec == QUERY_STYLE_NOTHING)
4221     {
4222         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4223         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4224     }
4225     else
4226     {
4227         sp_desktop_set_style (desktop, css, true, true);
4228     }
4230     sp_style_unref(query);
4232     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4233                                    _("Text: Change font family"));
4234     sp_repr_css_attr_unref (css);
4235     free (family);
4236     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4238     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4241 void
4242 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4243                                        GObject      *tbl)
4245     const char *family = gtk_entry_get_text (entry);
4247     try {
4248         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4249         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4250         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4251         gtk_tree_selection_select_path (selection, path.gobj());
4252         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4253         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4254     } catch (...) {
4255         if (family && strlen (family))
4256         {
4257             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4258         }
4259     }
4262 void
4263 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4264                                    gpointer          data)
4266     if (g_object_get_data (G_OBJECT (button), "block")) return;
4267     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4268     int prop = GPOINTER_TO_INT(data);
4270     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4271     SPCSSAttr *css = sp_repr_css_attr_new ();
4273     switch (prop)
4274     {
4275         case 0:
4276         {
4277             sp_repr_css_set_property (css, "text-anchor", "start");
4278             sp_repr_css_set_property (css, "text-align", "start");
4279             break;
4280         }
4281         case 1:
4282         {
4283             sp_repr_css_set_property (css, "text-anchor", "middle");
4284             sp_repr_css_set_property (css, "text-align", "center");
4285             break;
4286         }
4288         case 2:
4289         {
4290             sp_repr_css_set_property (css, "text-anchor", "end");
4291             sp_repr_css_set_property (css, "text-align", "end");
4292             break;
4293         }
4295         case 3:
4296         {
4297             sp_repr_css_set_property (css, "text-anchor", "start");
4298             sp_repr_css_set_property (css, "text-align", "justify");
4299             break;
4300         }
4301     }
4303     SPStyle *query =
4304         sp_style_new (SP_ACTIVE_DOCUMENT);
4305     int result_numbers =
4306         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4308     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4309     if (result_numbers == QUERY_STYLE_NOTHING)
4310     {
4311         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4312     }
4314     sp_style_unref(query);
4316     sp_desktop_set_style (desktop, css, true, true);
4317     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4318                                    _("Text: Change alignment"));
4319     sp_repr_css_attr_unref (css);
4321     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4324 void
4325 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4326                                gpointer          data)
4328     if (g_object_get_data (G_OBJECT (button), "block")) return;
4330     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4331     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4332     int          prop       = GPOINTER_TO_INT(data);
4333     bool         active     = gtk_toggle_button_get_active (button);
4335     SPStyle *query =
4336         sp_style_new (SP_ACTIVE_DOCUMENT);
4337     int result_fontspec =
4338         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4339     
4340     Glib::ustring fontSpec = query->text->font_specification.value;
4341     Glib::ustring newFontSpec;
4343     switch (prop)
4344     {
4345         case 0:
4346         {
4347             if (!fontSpec.empty()) {
4348                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4349             }
4350             if (fontSpec != newFontSpec) {
4351                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4352             }
4353             break;
4354         }
4356         case 1:
4357         {
4358             if (!fontSpec.empty()) {
4359                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4360             }
4361             if (fontSpec != newFontSpec) {
4362                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4363             }
4364             break;
4365         }
4366     }
4368     if (!fontSpec.empty()) {
4369         sp_repr_css_set_property (css, "-inkscape-font-specification", fontSpec.c_str()); 
4370     }
4372     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4373     if (result_fontspec == QUERY_STYLE_NOTHING)
4374     {
4375         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4376     }
4378     sp_style_unref(query);
4380     sp_desktop_set_style (desktop, css, true, true);
4381     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4382                                    _("Text: Change font style"));
4383     sp_repr_css_attr_unref (css);
4385     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4388 void
4389 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4390                                      gpointer         data)
4392     if (g_object_get_data (G_OBJECT (button), "block")) {
4393         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4394         return;
4395     }
4397     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4398     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4399     int          prop       = GPOINTER_TO_INT(data);
4401     switch (prop)
4402     {
4403         case 0:
4404         {
4405             sp_repr_css_set_property (css, "writing-mode", "lr");
4406             break;
4407         }
4409         case 1:
4410         {
4411             sp_repr_css_set_property (css, "writing-mode", "tb");
4412             break;
4413         }
4414     }
4416     SPStyle *query =
4417         sp_style_new (SP_ACTIVE_DOCUMENT);
4418     int result_numbers =
4419         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4421     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4422     if (result_numbers == QUERY_STYLE_NOTHING)
4423     {
4424         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4425     }
4427     sp_desktop_set_style (desktop, css, true, true);
4428     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4429                                    _("Text: Change orientation"));
4430     sp_repr_css_attr_unref (css);
4432     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4435 gboolean
4436 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4438     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4439     if (!desktop) return FALSE;
4441     switch (get_group0_keyval (event)) {
4442         case GDK_Escape: // defocus
4443             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4444             return TRUE; // I consumed the event
4445             break;
4446         case GDK_Return: // defocus
4447         case GDK_KP_Enter:
4448             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4449             return TRUE; // I consumed the event
4450             break;
4451     }
4452     return FALSE;
4455 gboolean
4456 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4458     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4459     if (!desktop) return FALSE;
4461     switch (get_group0_keyval (event)) {
4462         case GDK_Escape: // defocus
4463             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4464             sp_text_toolbox_selection_changed (NULL, tbl); // update
4465             return TRUE; // I consumed the event
4466             break;
4467     }
4468     return FALSE;
4471 gboolean
4472 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4474     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4475     if (!desktop) return FALSE;
4477     switch (get_group0_keyval (event)) {
4478         case GDK_KP_Enter:
4479         case GDK_Return:
4480         case GDK_Escape: // defocus
4481             gtk_widget_hide (w);
4482             visible = false;
4483             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4484             return TRUE; // I consumed the event
4485             break;
4486     }
4487     return FALSE;
4491 void
4492 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4493                                GObject     *tbl)
4495     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4497     if (g_object_get_data (tbl, "size-block")) return;
4499     char *text = gtk_combo_box_get_active_text (cbox);
4501     SPCSSAttr *css = sp_repr_css_attr_new ();
4502     sp_repr_css_set_property (css, "font-size", text);
4503     free (text);
4505     SPStyle *query =
4506         sp_style_new (SP_ACTIVE_DOCUMENT);
4507     int result_numbers =
4508         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4510     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4511     if (result_numbers == QUERY_STYLE_NOTHING)
4512     {
4513         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4514     }
4516     sp_style_unref(query);
4518     sp_desktop_set_style (desktop, css, true, true);
4519     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4520                                    _("Text: Change font size"));
4521     sp_repr_css_attr_unref (css);
4524     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4525         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4528 void
4529 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4530                                          GObject            *tbl)
4532     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4533     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4534     int x, y;
4536     if (!visible)
4537     {
4538         gdk_window_get_origin (widget->window, &x, &y);
4539         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4540         gtk_widget_show_all (popdown);
4542         gdk_pointer_grab (widget->window, TRUE,
4543                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4544                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4545                                         GDK_POINTER_MOTION_MASK),
4546                           NULL, NULL, GDK_CURRENT_TIME);
4548         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4550         visible = true;
4551     }
4552     else
4553     {
4554         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4555         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4556         gtk_widget_hide (popdown);
4557         visible = false;
4558     }
4561 gboolean
4562 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4563                                  GdkEventFocus    */*event*/,
4564                                  GObject          */*tbl*/)
4566     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4567     return FALSE;
4570 gboolean
4571 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4572                                    GdkEventFocus    */*event*/,
4573                                    GObject          */*tbl*/)
4575     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4577     gtk_widget_hide (popdown);
4578     visible = false;
4579     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4580     return TRUE;
4583 void
4584 cell_data_func  (GtkTreeViewColumn */*column*/,
4585                  GtkCellRenderer   *cell,
4586                  GtkTreeModel      *tree_model,
4587                  GtkTreeIter       *iter,
4588                  gpointer           /*data*/)
4590     char        *family,
4591         *family_escaped,
4592         *sample_escaped;
4594     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4596     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4598     family_escaped = g_markup_escape_text (family, -1);
4599     sample_escaped = g_markup_escape_text (sample, -1);
4601     std::stringstream markup;
4602     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4603     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4605     free (family);
4606     free (family_escaped);
4607     free (sample_escaped);
4610 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4611     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4612     if (completion) {
4613         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4614         g_object_unref (completion);
4615     }
4618 GtkWidget*
4619 sp_text_toolbox_new (SPDesktop *desktop)
4621     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4623     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4624     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4626     GtkTooltips *tt = gtk_tooltips_new();
4627     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4629     ////////////Family
4630     //Window
4631     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4632     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4634     //Entry
4635     GtkWidget           *entry = gtk_entry_new ();
4636     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4637     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4638     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4639     gtk_entry_completion_set_text_column (completion, 0);
4640     gtk_entry_completion_set_minimum_key_length (completion, 1);
4641     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4642     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4643     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4644     aux_toolbox_space (tbl, 1);
4645     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4646     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4648     //Button
4649     GtkWidget   *button = gtk_button_new ();
4650     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4651     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4653     //Popdown
4654     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4655     GtkWidget           *treeview = gtk_tree_view_new ();
4657     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4658     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4659     gtk_tree_view_column_pack_start (column, cell, FALSE);
4660     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4661     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4662     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4664     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4665     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4666     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4668     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4670     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4671     gtk_container_add (GTK_CONTAINER (sw), treeview);
4673     gtk_container_add (GTK_CONTAINER (window), sw);
4674     gtk_widget_set_size_request (window, 300, 450);
4676     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4677     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4678     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4680     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4682     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4683     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4685     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4686     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4688     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4689     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4690     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4691     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4692     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4694     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4695     aux_toolbox_space (tbl, 1);
4696     GtkWidget *box = gtk_event_box_new ();
4697     gtk_container_add (GTK_CONTAINER (box), image);
4698     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4699     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4700     GtkTooltips *tooltips = gtk_tooltips_new ();
4701     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4702     gtk_widget_hide (GTK_WIDGET (box));
4703     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4705     ////////////Size
4706     const char *sizes[] = {
4707         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4708         "16", "18", "20", "22", "24", "28",
4709         "32", "36", "40", "48", "56", "64", "72", "144"
4710     };
4712     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4713     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4714     gtk_widget_set_size_request (cbox, 80, -1);
4715     aux_toolbox_space (tbl, 1);
4716     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4717     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4718     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4719     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4721     //spacer
4722     aux_toolbox_space (tbl, 4);
4723     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4725     ////////////Text anchor
4726     GtkWidget *group   = gtk_radio_button_new (NULL);
4727     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4728     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4730     // left
4731     GtkWidget *rbutton = group;
4732     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4733     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4734     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4736     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4737     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4738     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4739     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4741     // center
4742     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4743     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4744     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4745     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4747     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4748     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4749     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4750     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4752     // right
4753     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4754     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4755     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4756     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4758     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4759     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4760     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4761     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4763     // fill
4764     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4765     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4766     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4767     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4769     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4770     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4771     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4772     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4774     aux_toolbox_space (tbl, 1);
4775     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4777     //spacer
4778     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4780     ////////////Text style
4781     row = gtk_hbox_new (FALSE, 4);
4783     // bold
4784     rbutton = gtk_toggle_button_new ();
4785     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4786     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4787     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4788     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4790     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4791     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4792     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4794     // italic
4795     rbutton = gtk_toggle_button_new ();
4796     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4797     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4798     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4799     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4801     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4802     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4803     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4805     aux_toolbox_space (tbl, 1);
4806     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4808     //spacer
4809     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4811     ////////////Text orientation
4812     group   = gtk_radio_button_new (NULL);
4813     row     = gtk_hbox_new (FALSE, 4);
4814     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4816     // horizontal
4817     rbutton = group;
4818     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4819     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4820     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4821     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4823     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4824     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4825     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4827     // vertical
4828     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4829     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4830     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4831     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4832     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4834     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4835     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4836     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4837     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4840     //watch selection
4841     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4843     sigc::connection *c_selection_changed =
4844         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4845                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4846     pool->add_connection ("selection-changed", c_selection_changed);
4848     sigc::connection *c_selection_modified =
4849         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4850                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4851     pool->add_connection ("selection-modified", c_selection_modified);
4853     sigc::connection *c_subselection_changed =
4854         new sigc::connection (desktop->connectToolSubselectionChanged
4855                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4856     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4858     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4861     gtk_widget_show_all (tbl);
4862     return tbl;
4864 } // end of sp_text_toolbox_new()
4866 }//<unnamed> namespace
4869 //#########################
4870 //##      Connector      ##
4871 //#########################
4873 static void sp_connector_path_set_avoid(void)
4875     cc_selection_set_avoid(true);
4879 static void sp_connector_path_set_ignore(void)
4881     cc_selection_set_avoid(false);
4886 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4888     // quit if run by the _changed callbacks
4889     if (g_object_get_data( tbl, "freeze" )) {
4890         return;
4891     }
4893     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4894     SPDocument *doc = sp_desktop_document(desktop);
4896     if (!sp_document_get_undo_sensitive(doc))
4897     {
4898         return;
4899     }
4901     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4903     if ( repr->attribute("inkscape:connector-spacing") ) {
4904         gdouble priorValue = gtk_adjustment_get_value(adj);
4905         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4906         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4907             return;
4908         }
4909     } else if ( adj->value == defaultConnSpacing ) {
4910         return;
4911     }
4913     // in turn, prevent callbacks from responding
4914     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4916     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4917     SP_OBJECT(desktop->namedview)->updateRepr();
4919     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4920     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4921         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4922         NR::Matrix m = NR::identity();
4923         avoid_item_move(&m, item);
4924     }
4926     if (items) {
4927         g_slist_free(items);
4928     }
4930     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4931             _("Change connector spacing"));
4933     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4935     spinbutton_defocus(GTK_OBJECT(tbl));
4938 static void sp_connector_graph_layout(void)
4940     if (!SP_ACTIVE_DESKTOP) return;
4942     // hack for clones, see comment in align-and-distribute.cpp
4943     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4944     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4946     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4948     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4950     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4953 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4955     if ( gtk_toggle_action_get_active( act ) ) {
4956         prefs_set_string_attribute("tools.connector", "directedlayout",
4957                 "true");
4958     } else {
4959         prefs_set_string_attribute("tools.connector", "directedlayout",
4960                 "false");
4961     }
4964 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4966     if ( gtk_toggle_action_get_active( act ) ) {
4967         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4968                 "true");
4969     } else {
4970         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4971                 "false");
4972     }
4976 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4978     prefs_set_double_attribute("tools.connector", "length", adj->value);
4979     spinbutton_defocus(GTK_OBJECT(tbl));
4982 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4983                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
4984                                             bool /*is_interactive*/, gpointer data)
4986     GtkWidget *tbl = GTK_WIDGET(data);
4988     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4989         return;
4990     }
4991     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4992         return;
4993     }
4995     GtkAdjustment *adj = (GtkAdjustment*)
4996             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4997     gdouble spacing = defaultConnSpacing;
4998     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5000     gtk_adjustment_set_value(adj, spacing);
5004 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5005     NULL, /* child_added */
5006     NULL, /* child_removed */
5007     connector_tb_event_attr_changed,
5008     NULL, /* content_changed */
5009     NULL  /* order_changed */
5010 };
5013 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5015     {
5016         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5017                                           _("Avoid"),
5018                                           _("Make connectors avoid selected objects"),
5019                                           "connector_avoid",
5020                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5021         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5022         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5023     }
5025     {
5026         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5027                                           _("Ignore"),
5028                                           _("Make connectors ignore selected objects"),
5029                                           "connector_ignore",
5030                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5031         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5032         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5033     }
5035     EgeAdjustmentAction* eact = 0;
5037     // Spacing spinbox
5038     eact = create_adjustment_action( "ConnectorSpacingAction",
5039                                      _("Connector Spacing"), _("Spacing:"),
5040                                      _("The amount of space left around objects by auto-routing connectors"),
5041                                      "tools.connector", "spacing", defaultConnSpacing,
5042                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5043                                      0, 100, 1.0, 10.0,
5044                                      0, 0, 0,
5045                                      connector_spacing_changed, 1, 0 );
5046     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5048     // Graph (connector network) layout
5049     {
5050         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5051                                           _("Graph"),
5052                                           _("Nicely arrange selected connector network"),
5053                                           "graph_layout",
5054                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5055         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5056         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5057     }
5059     // Default connector length spinbox
5060     eact = create_adjustment_action( "ConnectorLengthAction",
5061                                      _("Connector Length"), _("Length:"),
5062                                      _("Ideal length for connectors when layout is applied"),
5063                                      "tools.connector", "length", 100,
5064                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5065                                      10, 1000, 10.0, 100.0,
5066                                      0, 0, 0,
5067                                      connector_length_changed, 1, 0 );
5068     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5071     // Directed edges toggle button
5072     {
5073         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5074                                                       _("Downwards"),
5075                                                       _("Make connectors with end-markers (arrows) point downwards"),
5076                                                       "directed_graph",
5077                                                       Inkscape::ICON_SIZE_DECORATION );
5078         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5080         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5081         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5082                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5084         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5085     }
5087     // Avoid overlaps toggle button
5088     {
5089         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5090                                                       _("Remove overlaps"),
5091                                                       _("Do not allow overlapping shapes"),
5092                                                       "remove_overlaps",
5093                                                       Inkscape::ICON_SIZE_DECORATION );
5094         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5096         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5097         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5098                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5100         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5101     }
5103     // Code to watch for changes to the connector-spacing attribute in
5104     // the XML.
5105     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5106     g_assert(repr != NULL);
5108     purge_repr_listener( holder, holder );
5110     if (repr) {
5111         g_object_set_data( holder, "repr", repr );
5112         Inkscape::GC::anchor(repr);
5113         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5114         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5115     }
5116 } // end of sp_connector_toolbox_prep()
5119 //#########################
5120 //##     Paintbucket     ##
5121 //#########################
5123 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5125     gint channels = ege_select_one_action_get_active( act );
5126     flood_channels_set_channels( channels );
5129 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5131     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5134 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5136     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5139 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5141     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5142     SPUnit const *unit = tracker->getActiveUnit();
5144     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5146     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5149 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5151     // FIXME: make defaults settable via Inkscape Options
5152     struct KeyValue {
5153         char const *key;
5154         double value;
5155     } const key_values[] = {
5156         {"threshold", 15},
5157         {"offset", 0.0}
5158     };
5160     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5161         KeyValue const &kv = key_values[i];
5162         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5163         if ( adj ) {
5164             gtk_adjustment_set_value(adj, kv.value);
5165         }
5166     }
5168     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5169     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5170     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5171     ege_select_one_action_set_active( autogap_action, 0 );
5174 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5176     EgeAdjustmentAction* eact = 0;
5178     {
5179         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5181         GList* items = 0;
5182         gint count = 0;
5183         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5184         {
5185             GtkTreeIter iter;
5186             gtk_list_store_append( model, &iter );
5187             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5188             count++;
5189         }
5190         g_list_free( items );
5191         items = 0;
5192         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5193         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5194         ege_select_one_action_set_appearance( act1, "compact" );
5195         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5196         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5197         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5198         g_object_set_data( holder, "channels_action", act1 );
5199     }
5201     // Spacing spinbox
5202     {
5203         eact = create_adjustment_action(
5204             "ThresholdAction",
5205             _("Fill Threshold"), _("Threshold:"),
5206             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5207             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5208             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5209             0, 0, 0,
5210             paintbucket_threshold_changed, 1, 0 );
5212         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5213     }
5215     // Create the units menu.
5216     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5217     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5218     g_object_set_data( holder, "tracker", tracker );
5219     {
5220         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5221         gtk_action_group_add_action( mainActions, act );
5222     }
5224     // Offset spinbox
5225     {
5226         eact = create_adjustment_action(
5227             "OffsetAction",
5228             _("Grow/shrink by"), _("Grow/shrink by:"),
5229             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5230             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5231             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5232             0, 0, 0,
5233             paintbucket_offset_changed, 1, 2);
5234         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5236         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5237     }
5239     /* Auto Gap */
5240     {
5241         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5243         GList* items = 0;
5244         gint count = 0;
5245         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5246         {
5247             GtkTreeIter iter;
5248             gtk_list_store_append( model, &iter );
5249             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5250             count++;
5251         }
5252         g_list_free( items );
5253         items = 0;
5254         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5255         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5256         ege_select_one_action_set_appearance( act2, "compact" );
5257         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5258         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5259         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5260         g_object_set_data( holder, "autogap_action", act2 );
5261     }
5263     /* Reset */
5264     {
5265         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5266                                           _("Defaults"),
5267                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5268                                           GTK_STOCK_CLEAR );
5269         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5270         gtk_action_group_add_action( mainActions, act );
5271         gtk_action_set_sensitive( act, TRUE );
5272     }
5276 /*
5277   Local Variables:
5278   mode:c++
5279   c-file-style:"stroustrup"
5280   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5281   indent-tabs-mode:nil
5282   fill-column:99
5283   End:
5284 */
5285 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :